Skip to content

Instantly share code, notes, and snippets.

@MatthieuSarter
Last active February 1, 2019 18:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MatthieuSarter/73e5ed0133722512bfde90e206abd806 to your computer and use it in GitHub Desktop.
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.
#!/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