dimanche 23 mars 2014

Nmap, scapy, python

S'il y a bien un outil essentiel lors d'un pentest, il s'agit de nmap. Maîtriser cet outil peut permettre de gagner du temps et obtenir plus d'information lors de la phase de reconnaissance d'un audit. Mais j'ai l'impression qu'il a perdu en fiabilité ces derniers temps.

En particulier, je déteste le fait qu'un plantage en fin de scan sur des milliers d'adresses perdent le résultat des machines correctement scannées. Et des plantages, ce n'est pas rare. C'est d'ailleurs extrêmement courant en UDP et avec des scripts NSE comme les enumérations netbios.

Sans quand on recherche l'exhaustivité totale, il est devenu plus logique de faire un premier scan de détection des live hosts. Là encore nmap est la référence, mais il risque de se faire détrôner par zmap ou masscan (que je n'ai pas testé de peur de faire tomber les réseaux). Pour nmap, je n'ai jamais réussi à maîtriser totalement les retry et timeout, ce qui fait que la durée des scans dérive parfois énormément. J'ai finalement opté pour mon programme en python/scapy.

Je commence par définir les paquets que je souhaite envoyer avec quelques informations :
ICMP 8
ICMP 13
ICMP 17
TCP 80 S
TCP 80 A
TCP 443 S
TCP 443 A
UDP 53
UDP 161

Puis une liste d'IP ou de hostname :
example.com
example.com
google.com
scanme.insecure.org

Et enfin mon programme qui enverra uniquement les paquets que j'ai défini et rien d'autre :

#!/usr/bin/python
# -*- coding: utf-8 -*-

import argparse
from threading import *
from scapy.all import *
import Queue
import time

conf.verb = 0
conf.noenum.add(TCP.sport, TCP.dport, UDP.sport, UDP.dport, ICMP.type)
maxConnections = 100
timeoutPing = 3
connection_lock = BoundedSemaphore(value=maxConnections)
logQueue = Queue.Queue()
threads = []
endScript = False

def printThread(logQueue,logFile):
     while True:
        if not logQueue.empty():
            val = logQueue.get()
            logFile.write(val+"\n")
            print val
        else:
            if endScript:
                break 
            time.sleep(2)

def sendPacketICMP(packet,logQueue):
    ans=sr1(packet,timeout=timeoutPing)
    text=''
    if ans:
        text =  '[+] ' + packet.sprintf("%IP.dst% ICMP/%ICMP.type%") + \
                ' > ' + ans.sprintf("%IP.src% ICMP/%ICMP.type%")
        logQueue.put(text)
    else:
        text = '[-] ' + packet.sprintf("%IP.dst% ICMP/%ICMP.type%") + ' > No response'
        logQueue.put(text)

def sendPacketTCP(packet,logQueue):
    ans=sr1(packet,timeout=timeoutPing)
    text=''
    if ans:
        text =  '[+] ' + packet.sprintf("%IP.dst% TCP/%TCP.dport%/%TCP.flags%") + \
                 ' > ' + ans.sprintf("{IP:%IP.src% {ICMP:ICMP/%ICMP.type%}{TCP:TCP/%TCP.sport%/%TCP.flags%}}")
        logQueue.put(text)
    else:
        text = '[-] ' + packet.sprintf("%IP.dst% TCP/%TCP.dport%/%TCP.flags%") + ' > No response'
        logQueue.put(text)

def sendPacketUDP(packet,logQueue):
    ans=sr1(packet,timeout=timeoutPing)
    text=''
    if ans:
        text =  '[+] ' + packet.sprintf("%IP.dst% UDP/%UDP.dport%") + \
                 ' > ' + ans.sprintf("{IP:%IP.src% {ICMP:ICMP/%ICMP.type%}{UDP:UDP/%UDP.sport%}}")
        logQueue.put(text)
    else:
        text = '[-] ' + packet.sprintf("%IP.dst% UDP/%UDP.dport%") + ' > No response'
        logQueue.put(text)

def sendPacket(ip,logQueue,proto,port,flag):
    if proto=='ICMP':
        packet=IP(dst=ip)/ICMP(type=port)
        sendPacketICMP(packet,logQueue)
    elif proto=='TCP':
        packet=IP(dst=ip)/TCP(dport=port,flags=flag)
        sendPacketTCP(packet,logQueue)
    elif proto=='UDP':
        packet=IP(dst=ip)/UDP(dport=port)
        sendPacketUDP(packet,logQueue)

def ping_ip(ip,logQueue,config):
    try:
        for p in config:
            if len(p)==2:
                sendPacket(ip,logQueue,p[0],int(p[1]),'')
            else:
                sendPacket(ip,logQueue,p[0],int(p[1]),p[2])

    finally:
        connection_lock.release()

def main():
    parser = argparse.ArgumentParser(description='Superping')
    parser.add_argument('-i', metavar='ip list', type=argparse.FileType('rt'), required=True)
    parser.add_argument('-c', metavar='config', type=argparse.FileType('rt'), required=True)
    parser.add_argument('-o', metavar='out-file', type=argparse.FileType('wt'), required=True)
    parser.add_argument('--version', action='version', version='%(prog)s 1.0')

    try:
        results = parser.parse_args()
    except IOError, msg:
        parser.error(str(msg))

    lc = sum(1 for l in results.c)
    results.c.seek(0, 0)
    print '[*] Chargement de '+ str(lc) +' types de ping'    
    config = [line.strip().split() for line in results.c.readlines()]
    
    lc = sum(1 for l in results.i)
    results.i.seek(0, 0)
    print '[*] Lancement du scan de '+ str(lc) +' machines'
    
    t = Thread(target=printThread,args=(logQueue,results.o))
    t.daemon = True
    child = t.start()   
    
    lines = results.i.readlines()
    for ip_line in lines:
        connection_lock.acquire()
        t = Thread(target=ping_ip,\
          args=(ip_line.rstrip('\n'),logQueue,config))
        t.daemon = True
        child = t.start()
        threads.append(t)

    [x.join() for x in threads]
    endScript = True
    print '[*] Fin des threads'

if __name__ == '__main__':
    main()

La commande suivante me sort le résultat attendu :

./superping.py -i ip_list -c config -o log 2> /dev/null
[*] Chargement de 9 types de ping
[*] Lancement du scan de 3 machines
[+] Net('google.com') ICMP/8 > 74.125.132.138 ICMP/0
[+] Net('example.com') ICMP/8 > 93.184.216.119 ICMP/0
[+] Net('scanme.insecure.org') ICMP/8 > 74.207.244.221 ICMP/0
[+] Net('scanme.insecure.org') ICMP/13 > 74.207.244.221 ICMP/14
[-] Net('google.com') ICMP/13 > No response
[-] Net('example.com') ICMP/13 > No response
[-] Net('scanme.insecure.org') ICMP/17 > No response
[+] Net('scanme.insecure.org') TCP/80/S > 74.207.244.221 TCP/80/SA
[-] Net('google.com') ICMP/17 > No response
[-] Net('example.com') ICMP/17 > No response
[+] Net('google.com') TCP/80/S > 74.125.132.138 TCP/80/SA
[+] Net('example.com') TCP/80/S > 93.184.216.119 TCP/80/RA
[+] Net('google.com') TCP/80/A > 74.125.132.139 TCP/80/R
[-] Net('scanme.insecure.org') TCP/80/A > No response
[+] Net('example.com') TCP/80/A > 93.184.216.119 TCP/80/R
[+] Net('google.com') TCP/443/S > 74.125.132.113 TCP/443/SA
[+] Net('scanme.insecure.org') TCP/443/S > 74.207.244.221 TCP/443/RA
[+] Net('example.com') TCP/443/S > 93.184.216.119 TCP/443/RA
[+] Net('example.com') TCP/443/A > 93.184.216.119 TCP/443/R
[+] Net('google.com') TCP/443/A > 74.125.132.138 TCP/443/R
[+] Net('scanme.insecure.org') TCP/443/A > 74.207.244.221 TCP/443/R
[+] Net('scanme.insecure.org') UDP/53 > 74.207.244.221 ICMP/3
[+] Net('scanme.insecure.org') UDP/161 > 74.207.244.221 ICMP/3
[-] Net('google.com') UDP/53 > No response
[-] Net('example.com') UDP/53 > No response
[*] Fin des threads

Et voilà ! On obtient une jolie liste des IP up qu'on va pouvoir scanner intégralement avec Nmap.

vendredi 1 novembre 2013

Mon petit 0day pour l'ANSSI :)

Au cours d'un test un peu farfelu je suis tombé sur une faille un peu inclassable et un peu inutile :)
Mais finalement vu tous les articles sur la "privacy" qu'on peut lire actuellement, je vais balancer mon 0day... et puis je doute obtenir un bug bounty de Microsoft.

Je faisais de la classification d'informations recueillies avec FOCA et j'ai creusé un peu plus que nécessaire dans les metadata des documents récupérés. Dans un premier temps j'ai fait une passe sur les metadata des fichiers PDF avec pdfinfo, puis j'ai fait la même chose sur les images avec exiv2. Et là je me suis dit. Est-ce qu'on ne pourrait pas chopper les données exif des images contenues dans les pdf ???

Rien de plus simple ! Il existe un outil qui s'appelle pdfimages pour extraire les images des pdf. Alors en route.

#!/bin/bash

# dependances:
# md5deep
# poppler-utils -> pdfimages

mkdir images
for file in *.pdf
do
 echo ${file}
 pdfimages -j "${file}" ./images/"${file}"
 #on vire les bouzins n&b
 find ./images/ -name "${file}*.pbm" -exec rm -f {} \; 
 #ppm
 find ./images/ -name "${file}*.ppm" -size -300k -exec rm -f {} \; 
 #jpg
 find ./images/ -name "${file}*.jpg" -size -20k -exec rm -f {} \;
 #moulinette pour virer les doublons
 md5deep "./images/${file}"* | sort | while read arg1 arg2
 do
  newsum=$arg1
  if [ "$newsum" = "$oldsum" ]
  then
   rm  "$arg2"
  fi
  oldsum="$newsum"
 done
done
#doublons inter documents
md5deep ./images/* | sort | while read arg1 arg2
do
 newsum=$arg1
 if [ "$newsum" = "$oldsum" ]
 then
  rm  "$arg2"
 fi
 oldsum="$newsum"
done

Ce script va extraire les images dans un sous-répertoire, ma première idée était de visualiser les images pour ne pas à me palucher 800 documents à lire. Donc j'ai mis une moulinette qui supprime les images trop petites. A vous de voir la quantité d'images que vous obtenez.
A noter que PDF Creator simule une imprimante virtuelle. J'ai l'impression qu'il fait des miettes d'images minuscules (des milliers). C'est pour cela que j'ai viré les petites images.

Puis enfin je passe le script exif sur les images :

#!/bin/bash
# dependance: exiv2

for file in *.jpg
do
 nbLine=$(exiv2 ${file} | grep -c "Exif")
 if [ "${nbLine}" -ne 0 ]
 then
  echo ${file}
  exiv2 -pt -u "${file}"
 fi
done

Si je prends par exemple un fichier appelé ANSSI_Presentation_PASSI_du_10_sept_2013.pdf, j'obtiens les informations suivantes avec pdfinfo :

pdfinfo ANSSI_Presentation_PASSI_du_10_sept_2013.pdf 
Creator:        Microsoft® PowerPoint® 2010

Et la sortie de mon script :

Exif.Image.0x0302                            Ascii      22  Photoshop ICC profile
Exif.Image.Software                          Ascii      37  Adobe Photoshop Elements 8.0 Windows
Exif.Image.DateTime                          Ascii      20  2011:08:09 09:08:37
Exif.Photo.UserComment                       Undefined 122  CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 90

Rien de vraiment intéressant, mais une belle preuve de concept montrant qu'Office 2010 ne supprime pas les données Exif des images intégrées dans les documents lorsqu'ils sont enregistrés en PDF. Je n'ai constaté le même résultat avec PDF creator ou PDF distiller. Je n'ai pas non plus tester sur un fichier issu d'Office 2013.

lundi 9 septembre 2013

CVSS ou pas ?

Je suis actuellement sur une campagne de 15 pentests pour un client qui veut que l'on note chaque vulnérabilité avec un niveau CVSS. L'exercice est assez particulier et m'a fait perdre la foi dans cette métrique :)

Armé de mon petit calculateur de CVSS que j'ai développé en moins de 30 minutes en utilisant le manuel de référence, je m'en vais mettre des notes à mes vulns et là c'est la douche froide.
  • Comment noter une fonctionnalité dangeureuse comme le transfert automatique de message dans Exchange ?
  • Comment noter une faille dans un système d'authentification forte qui réduit alors l'authentification au seul mot de passe ?
  • Comment qualifier le fait qu'un utilisateur puisse accéder aux interfaces d'administration d'un serveur ?
  • Comment noter un bête XSS sur une applications interne ?
Il n'y a pas de réponse pour les 3 premiers points, mais on peut développer un peu sur le XSS.
  • Faut-il considérer l'attaque depuis le réseau (Internet) ou depuis un adjacent network ?
  • Est-ce que l'Access Complexity est élevé car nécessitant une phase d'ingénieurie sociale (phishing) ou moyenne car c'est un bête XSS sur un moteur de recherche ?
  • Avec un XSS on peut voler le cookie d'une personne et accéder à l'application avec ses droits. On peut altérer ses données. Avec le cookie d'un admin on peut supprimer la base de données. Sauf qu'on attaque personne par personne. Comment qualifier l'impact DIC de façon pertinente ?
Bref, je finis par faire discrètement à ma sauce pour obtenir le niveau de criticité que j'estime satisfaisant. Mais parfois on tombe sur une vulnérabilité connue qui remonte dans un scan Nessus et là on doit accepter la criticité définie par l'éditeur qui peut être surestimée par précaution (ou le contraire). Par exemple les failles sur HP SMH peuvent aller jusqu'à 9.0 alors qu'il faut déjà avec un compte utilisateur sur un application de management de serveurs... alors qu'un XSS sans authentification sur la même application n'atteint que 4,3 et au passage la complexité est moyenne alors qu'il faut faire de l'ingénieurie sociale et qu'au final on pourrait voler un compte admin et compromettre totalement le serveur.

Un peu comme je l'évoque pour le XSS, il existe des scénarios qui enchaînent les failles. Par exemple une interface admin accessible, un contournement d'authentification, une élévation de privilège et compromission totale du serveur. Et puis quoi ? Avec des si on mettrait Paris en bouteille.

Ce dont il faut bien se souvenir quand on utilise CVSS, c'est qu'on mesure la criticité d'une vulnérabilité, on ne mesure pas le risque. Est-ce que l'entreprise est grande, a beaucoup de concurrence, a une population technophile (SSII), qui aime assez peu sa société (mauvaise rémunération) ? CVSS n'en tient pas compte. C'est à l'auditeur de faire une analyse de risque et de faire ressortir ce qui est réellement critique dans le contexte de l'entreprise.

mardi 20 août 2013

Review: The practice of network security monitoring

Je suis un fidèle lecteur de Richard Bejtlich et j'avais beaucoup aimé The art of network security monitoring. C'est pourquoi j'ai sauté sur son nouveau bouquin The practice of network security monitoring.

Pour tout vous dire, je l'ai reçu samedi et fini ce soir. Finalement autant parce que j'en attendais beaucoup que parce qu'il est creux. En effet une des idées que je défends et que Richard défend également est qu'il vaut mieux connaître des techniques et des stratégies que de savoir utiliser bêtement des outils.

Et son livre en est presque l'exemple opposé. Je vais un peu détailler les chapitres. (Le sommaire complet du livre est ici.)

Partie 1 (50 pages) : 
Chapitre 1 : On en retient qu'il n'y a pas de défense parfaite et qu'il faut monitorer son réseau pour savoir ce que fait le pirate, comment il est rentré et ce qu'il a volé. La sécurité est un cercle d'amélioration continu...

Chapitre 2 :  Le NAT c'est pas cool il faut sniffer là où ce n'est pas NATé donc derrière le firewall sur chaque patte interne alors que sans NAT on aurait pu sniffer simplement sur la patte externe.

Partie 2 (60 pages) :
J'installe Security Onion en local ou en distribué (juste des options à cocher lors de l'installation).

Partie 3 (70 pages) :
Très légère introduction pleine de capture d'écran aux outils suivants : tcpdump, dumpcap et tshark, Argus et Ra, Wireshark, Xplico, NetworkMiner, Squil, Squert, Snorby, ELSA.
Vous en apprendrez largement autant en cherchant chaque nom sur Google.

Partie 4 (120 pages) :
On commence enfin à voir de la valeur ajoutée.
Chapitre 9 : En 20 pages on a plein de définitions et le fonctionnement d'un CIRT. La finalité n'est pas de remonter des alertes, mais de traiter les alertes. C'est intéressant mais beaucoup trop court à mon goût.

Chapitre 10 : Compromission d'un serveur avec scan du réseau interne. Bonne démonstration de l'utilisation de Squil, Tshark et Bro. En plus macro on voit comment suivre les sessions pour identifier les rebonds et tracer les actions du pirate.

Chapitre 11 : Compromission d'un poste utilisateur et rebond sur un autre poste de travail. Belle démonstration de ELSA et Bro

Chapitre 12 : Comment dumper automatiquement les exe des communications réseau et envoyer manuellement les hash à virustotal.
Utilisation du module ATP1 (proof of concept) qui permet de rechercher des IOC (indicators of compromise) dans les différentes traces (DNS, certificats, MD5 de fichiers).
Utilisation de la MHR (malware hash registry) de la Team Cymru. qui fait un peu comme VirusTotal mais via des requêtes DNS.

Chapitre 13 : A l'instar du NAT, les proxy font sauter la correspondance des adresses IP. Bref il vaut mieux avoir accès aux logs du proxy.
Explication du mécanisme d'Offloading qui consiste à laisser la carte réseau calculer les checksums des paquets, ce qui fait que les tcpdump générés par le système d'exploitation sont à 0x0000. Ces fichiers sont parfois ignorés par les outils d'analyse dont Bro.

Conclusion : Ca va être compliqué avec le Cloud ?!

Bref, je n'ai trouvé que le chapitre 4 à mon goût, mais du coup c'est vraiment trop court pour assouvir ma faim. Je ne peux recommander ce livre qu'aux débutants qui auraient la volonté d'installer Security Onion, mais pas aux personnes qui connaissent le sujet et qui auraient voulu y découvrir de nouvelles techniques.

Update 22/08/13 : Un post sur le blog pauldotcom confirme exactement mon avis, mais en étant plus gentil.

jeudi 15 août 2013

La gestion risque/coût au quotidien

C'est étrange comment pour économiser quelques euros on peut intuitivement faire des paris et risquer de perdre plus.

Comme tous les 2 ans j'ai emmené ma voiture au contrôle technique et j'ai hésité à faire une pré-visite dans un garage. D'un côté je sais, je sens, qu'elle roule bien. Elle n'a que 7 ans. Il y a bien un impact sur le pare-brise, mais il est net et ne devrait pas se fissurer. Puis en fait la franchise est la même pour son remplacement ou pour une injection de résine... Alors ça peut attendre.

Bref je prends le risque d'emmener ma voiture directement au contrôle technique, mais je sais que je peux au moins contrôler les feux moi-même. Et ça n'a pas raté ! J'avais un feux de position et un feux de brouillard arrière grillés... Surtout que ça peut se changer tout seul. Bref je change les ampoules, mais le feux de brouillard ne veut rien savoir. Un tour sur Internet et je découvre d'une part qu'il n'est pas contrôlé et d'autre part... qu'il n'est pas utilisé, juste décoratif (pourquoi mettre une ampoule dans l'emplacement alors ?).

Résultat OK pour le contrôle technique. Ca aurait été bête de ne pas faire ce petit contrôle juste avant :)

PS : j'ai pris une ampoule Norauto et non Philips pour gagner 5€. Là encore je prends un risque, mais d'un autre côté ils les vendent par pack de 2. J'ai une marge de manœuvre :D

Pentest sur la production ou non ?

Un nouvel appel d'offre tombe, on nous propose d'auditer un système de gestion de chèques. Il est demander de faire des tests de corruption de données. "Avez-vous des questions ? - Oui, s'agira-t-il d'un système en production ? - Tout à fait cela sera en production."

Très bien monsieur le client ! Vos désirs seront nos ordres. Il n'est évidemment pas de notre ressort de contredire le client surtout dans un appel d'offre... Mais juste juste pour rappel, ce n'est pas parce qu'on est assuré professionnellement pour ce type d'activité qu'on sera tenu responsable en cas d'incident. En effet le client est responsable de ce qu'il demande de faire sur son SI. Le pentester doit rappeler les risques et le client valide (accepte de prendre la responsabilité). Lorsqu'on veut faire un débordement de tampon, on pose la question avant le test en précisant que le service peut devenir instable et qu'il faudra peut-être le relancer par la suite. Le pentester se dégage alors de toute responsabilité si le client accepte de faire le test.

Cela étant dit, pourquoi les clients nous demandent de faire des tests en production ? Est-ce mieux ou moins bien que sur un système en pré-production ?

Je vois plusieurs raisons à ce problème :
  • On n'a pas de pré-production :)
    Une recette alors ? Euh chez le prestataire chargé du développement, mais on n'y a pas accès.
    Il y a effectivement encore beaucoup de systèmes gérés à l'arrache sans environnements de pré-production et recette. Comment font les équipes pour être sûres qu'une mise à jour fonctionnera correctement ? Ca pourrait être une vulnérabilité à mettre dans notre rapport...
    Alors faites bien une sauvegarde avant l'audit et chaque soir. Oui, il faut le rappeler... Et donnez-nous un contact à appeler si le système devient indisponible.
    Bref on bosse sur un système vivant, mais avec beaucoup de contraintes et de risques.
  • On a un système qui vient d'être livré (en production) et pas encore d'utilisateurs, donc aucun impact sur la production. Sauf qu'il est vierge... Les bases de données sont vides.
    Dans certains cas cela n'est pas gênant, mais de plus en plus de fonctions ne sont accessibles que lorsque des données sont présentes en base. Il faut demander à ce que de fausses données soient présentes ou bien une copie de la production.
    Il m'est arrivé plusieurs fois récemment de faire un pentest à la fin du développement d'un projet, avant qu'il soit mis en production. Il n'y a rien de pire que de tester un système vierge, par exemple un site de recrutement dans lequel il n'y a pas d'offre d'emplois et aucun profil de candidat... Il faut alors se palucher les modes d'emploi et découvrir le fonctionnement du site.
    Encore pire, le système était en cours de livraison. Les fonctions arrivaient les unes après les autres. Le lundi on voyait une page d'erreur avec un mot de passe en clair, puis le mardi une nouvelle partie du site apparaît à la place...
  • On a bien un système en pré-production, mais il n'est pas interfacé avec le même annuaire et avec les mêmes webservices.
    A la rigueur on peut travailler en pré-production, puis faire les tests de contournement d'authentification en production.
  • On a un système en production avec des données similaires à la production et toutes anonymisées.
    Voilà, on y est. C'est le Paradis. Sauf que j'en ai entendu parler et je ne l'ai jamais vu :)
Bref, le pentester a beaucoup de donne volonté, mais si on ne lui donne pas les bonnes conditions pour réaliser tous les tests correctement, il va éviter de faire ceux qui peuvent secouer la production comme une blind SQL injection ou alors il va rater des fonctions car elles n'étaient pas activées faute de données disponibles. Et on ne pourra pas le tenir responsable.

Alors cher client, si tu veux rentabiliser ton audit en optimisant le temps de travail des auditeurs pour qu'ils trouvent le plus possible de vulnérabilités, prépare leur un beau terrain de jeu en pré-production avec des données représentatives.

lundi 12 août 2013

Si c'est pas malheureux :-/

Bonjour M. xxx,
Nous avons été en contact en début d’année pour un poste d’ingénieur sécurité au sein de yyy.
Le process n’avait pas abouti car vous aviez préféré donner suite à une proposition de collaboration d’un de nos concurrents.
Néanmoins, j’avais vraiment apprécié nos échanges et je me permets donc de revenir vers vous aujourd’hui afin de vous demander si vous connaissez dans votre entourage des profils qui seraient susceptibles d’être intéressés par nos offres d’emploi. Ma cible première, concerne des candidats avec 3-6 ans d’expérience en sécurité réseau.
Je vous remercie beaucoup pour votre retour et pour votre aide,

Bien cordialement,

zzz | Chargée de Recrutement

------------------

J'en ai encore la larme à l'œil de cet appel en détresse. Comment transformer des candidats qui ne sont pas venus en chasseurs de tête gratuits ???
Un chasseur de tête est généralement payé entre 10 et 30% du salaire annuel du recruté alors qu'une cooptation peut aller de 300€ à 1500€.
Bref du gros foutage de gueule de SSII.