Solution de l’exercice d’hier sur shadow 9


Ça va de soit, mais ça va mieux en le disant, ceci n’est pas la solution unique de l’exercice d’hier, mais une solution possible parmi d’autres.

On note l’usage de crypt, qui évite de se faire chier à trouver le bon algo de hashing et gère le salt automatiquement. spwd, c’est vraiment pour le grosses larves comme moi qui veulent même pas faire un split.

Et c’est du Python 3, yo dog !

import io
import os
import crypt
import spwd
 
from urllib.request import FancyURLopener
from zipfile import ZipFile
 
PASSWORDS_SOURCE = "http://xato.net/files/10k%20most%20common.zip"
PASSWORDS_LIST = '10k most common.txt'
 
# Le fichier ZIP est derrière cloudflare, qui vous ferme la porte au nez si
# vous n'avez pas de User-Agent. On va donc créer un UrlOpener, un objet qui
# ouvre des ressources en utilisant leurs URLs, qui a un User-Agent 'TA MERE'.
# CloudFlare ne check pas que le UA est valide.
class FFOpener(FancyURLopener):
   version = 'TA MERE'
 
# Si le dictionnaire de passwords n'est pas là, on le télécharge
# via FFOpener().open(PASSWORDS_SOURCE).read(). C'est verbeux, c'est urllib.
# Normalement je ferais ça avec requests. Ensuite on lui donne une interface
# file-like object avec io.BytesIO pour que ZipFile puisse le traiter en mémoire
# sans avoir à le sauvegarder dans un vrai fichier sur le disque, et on
# extrait le ZIP.
if not os.path.isfile(PASSWORDS_LIST):
    ZipFile(io.BytesIO(FFOpener().open(PASSWORDS_SOURCE).read())).extractall()
 
# On extrait les mots de passe de la liste sous forme de tuple car c'est rapide
# à lire. Un petit rstrip vire les sauts de ligne.
passwords = tuple(l.rstrip() for l in open(PASSWORDS_LIST))
 
# spwd.getspall() nous évite de parser le fichier shadow à la main.
for entry in spwd.getspall():
    print('Processing password for user "%s": ' % entry.sp_nam, end='')
 
    # Pas de hash ? On gagne du temps avec 'continue'
    if not '$' in entry.sp_pwd:
        print('no password hash to process.')
        continue
 
    # On teste chaque password avec la fonction crypt, qui accepte en deuxième
    # paramètre le hash du mot de passe complet. Pas besoin de se faire chier
    # à le spliter, il va analyser les '$' et se démerder avec ça. On a juste
    # à comparer le résultat avec le hash d'origine.
    for pwd in passwords:
        if crypt.crypt(pwd, entry.sp_pwd) == entry.sp_pwd:
            print('password is "%s".' % pwd)
            # On break pour gagner quelques tours de boucles, et pouvoir
            # utiliser la condition 'else'.
            break
    else:
        print('fail to break password.')

Télécharger le code.

9 thoughts on “Solution de l’exercice d’hier sur shadow

  • foxmask

    Pour faire mon chieur :
    je ne sais pas si c’est lié à Python 3 mais

    , end=''

    ca fait syntaxe invalide en 2.7; apres je peux pas tester urllib.request qui n’existe qu’en 3.

    Du coup je voulais tester ce que tu voulais qu’on affiche quand on ne trouve pas de mot de passe :

    Processing password for user "root": no password hash to process.
    Processing password for user "daemon": no password hash to process.

    Mais il me semble que ton code afficherait plutôt

    Processing password for user "root":
    no password hash to process.
    Processing password for user "daemon":
    no password hash to process.

    Pour moi c’est pas pareil :) non ?

  • Rififi

    Mais j’ai une question tout de même. Ils sont pas censés être salés les mots de passe ? Et si oui, comment trouver le salt sur un fichier de hashes récupérer ici ou là ?

  • ashgan

    @Rififi: Sam a pris un cas special, le fichier /etc/shadow sous unix qui stocke le salt non crypte a cote du pass crypte. d’ou la facilite de decryptage.
    bien evidemment, sur un autre systeme d’autentification, le salt peut (doit!) ne pas etre stocke au meme endroit, etre crypte, et etre different suivant les users.

  • bob

    @foxmask c’est liée à python 3 qui dans la méthode print offre plus de souplesse sur la fin du print que python 2 qui met obligatoirement un retour chariot à la fin.
    Pour contourner ça tu peux mettre une virgule à la fin de l’instruction print dans python 2 .


    >>>>def bonjourBob():
    ... print "Bonjour",
    ... print "Bob"
    ...
    ...
    >>>> bonjourBob()
    Bonjour Bob

  • Syl

    Vu qu’il y a 10.000 pwds dans la liste, ça n’aurait pas été mieux de faire un générateur au lieu d’un tuple pour la variable ‘passwords’?

    ———————————————-
    Sinon petite question subsidiaire: y’a-t-il une quelconque différence ou convention concernant la position de ‘in’ dans un statement?
    => if a not in b
    ou?
    => if not a in b

    (oui, je sais, c’est de l’e*****ge de mouches!)
    ———————————————-

Leave a comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.