Last active
February 1, 2019 18:26
-
-
Save MatthieuSarter/73e5ed0133722512bfde90e206abd806 to your computer and use it in GitHub Desktop.
This software checks a list of plain text passwords against the leaked password database built by Troy Hunt.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# +---------------------------------------------------------------+ | |
# | pwnedpasscheck | | |
# +---------------------------------------------------------------+ | |
# | Copyright (C) Matthieu SARTER | | |
# +---------------------------------------------------------------+ | |
# | |
# This software is governed by the CeCILL-B license under French law and | |
# abiding by the rules of distribution of free software. You can use, | |
# modify and/or redistribute the software under the terms of the | |
# CeCILL-B license as circulated by CEA, CNRS and INRIA at the following | |
# URL "http://www.cecill.info". | |
''' | |
This software checks a list of plain text passwords against the leaked | |
password database built by Troy Hunt. | |
See : https://haveibeenpwned.com/Passwords | |
When using this software, your plain text passwords will remain on your | |
system, only a partial SHA-1 will be transmitted to the server. | |
Usage : | |
pwnedpasscheck <passwords file> [--csv [<pass col index>]] | |
<passwords file> The passwords list, 1 per line. | |
--csv Use this option to use a CSV file as input. | |
<pass col index> Indicates the index of the password column in the | |
CSV file, starting from 1. If not specified, the | |
index of 'pass' or 'password' in the first line | |
will be used. | |
''' | |
from collections import defaultdict, namedtuple | |
import codecs | |
import hashlib | |
import sys | |
import requests | |
PassInfos = namedtuple('PassInfos', ['plain', 'hash', 'line_number', 'line']) | |
def run(params): | |
if len(params) < 2: | |
print('Missing password file name !') | |
sys.exit(1) | |
passwords = defaultdict(lambda: list()) | |
csv = len(params) > 2 and params[2] == '--csv' | |
password_col = len(params) > 3 and params[3] | |
with codecs.open(params[1], "r", "utf-8") as pass_file: | |
header = True | |
line_number = 0 | |
for line in pass_file.readlines(): | |
line = line.strip() | |
line_number += 1 | |
if csv and header: | |
header = False | |
if not password_col: | |
row = line.lower().split(',') | |
row = list(map(str.strip, row)) | |
print(row) | |
password_col = ('pass' in row and row.index('pass')) or ('password' in row and row.index('password')) | |
if password_col is False: | |
print('Unable to guess the password column index, please enter it in the command line.') | |
sys.exit(1) | |
else: | |
password_col = int(password_col) - 1 | |
continue | |
try: | |
if csv: | |
password = line.split(',')[password_col] | |
else: | |
password = line | |
hash = hashlib.sha1(password.encode('utf8')).hexdigest().upper() | |
passwords[hash[0:5]].append(PassInfos(password, hash, line_number, line)) | |
except Exception as e: | |
print('Error reading line ' + str(line_number) + ': ' + str(e)) | |
for key, pass_infos in passwords.items(): | |
response = requests.get('https://api.pwnedpasswords.com/range/' + key) | |
pwned_list = response.content | |
for pwned_infos in pwned_list.splitlines(): | |
pwned_hash, frequency = pwned_infos.decode('utf8').split(':') | |
for pass_info in pass_infos: | |
if key + pwned_hash == pass_info.hash: | |
print('Oups ! Password ' + pass_info.plain + ' has been leaked ' + | |
frequency + ' time(s) :-( (line ' + str(pass_info.line_number) + ')') | |
if __name__ == "__main__": | |
run(sys.argv) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment