Compare commits
No commits in common. "master" and "beta" have entirely different histories.
1
Pipfile
1
Pipfile
@ -8,7 +8,6 @@ verify_ssl = true
|
|||||||
[packages]
|
[packages]
|
||||||
requests = "*"
|
requests = "*"
|
||||||
pyinstaller = "*"
|
pyinstaller = "*"
|
||||||
argparse = "*"
|
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.7"
|
python_version = "3.7"
|
||||||
|
11
Pipfile.lock
generated
11
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "72f7b87a032a3c10cfa8b0914121403db30a4f3582386cce2f516a1b7f897b42"
|
"sha256": "38e2af2d59158d85bb4d55eb3140a0745c9eb21635643df4b2192cdd0abc2074"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -23,14 +23,6 @@
|
|||||||
],
|
],
|
||||||
"version": "==0.16.1"
|
"version": "==0.16.1"
|
||||||
},
|
},
|
||||||
"argparse": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4",
|
|
||||||
"sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.4.0"
|
|
||||||
},
|
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
|
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
|
||||||
@ -47,6 +39,7 @@
|
|||||||
},
|
},
|
||||||
"future": {
|
"future": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
"sha256:1d73b8a1aab19cb8c2c961ba82bf93860e1fb7d361be21e7288691c068cd3cfc",
|
||||||
"sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"
|
"sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"
|
||||||
],
|
],
|
||||||
"version": "==0.17.1"
|
"version": "==0.17.1"
|
||||||
|
2
build.sh
Executable file → Normal file
2
build.sh
Executable file → Normal file
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
pyinstaller --distpath=../dist --workpath=../build -y ../src/have_I_b33n_pwned.py
|
pyinstaller --distpath=../dist --workpath=../build ../src/have_I_b33n_pwned.py
|
||||||
|
130
src/have_I_b33n_pwned.py
Executable file → Normal file
130
src/have_I_b33n_pwned.py
Executable file → Normal file
@ -21,83 +21,44 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# updater script for nextcloud
|
# updater script for nextcloud
|
||||||
#
|
#
|
||||||
#TODO
|
# TODO
|
||||||
|
# - add feature: also check for email breaches
|
||||||
# - add feature: keepass integration? isnt there something like this already?
|
# - add feature: keepass integration? isnt there something like this already?
|
||||||
# - handle mail API exeption more precisely
|
|
||||||
|
|
||||||
|
|
||||||
from os import name
|
|
||||||
from sys import argv, stdout, exit
|
from sys import argv, stdout, exit
|
||||||
from re import match
|
|
||||||
from argparse import ArgumentParser
|
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
import requests
|
from requests import get
|
||||||
|
|
||||||
|
RED = "\033[1;31m"
|
||||||
if name == 'nt': # disable colors for windows environment
|
GREEN = "\033[0;32m"
|
||||||
(RED, GREEN, RESET) = ("", "", "")
|
RESET = "\033[0;0m"
|
||||||
else:
|
API = 'https://api.pwnedpasswords.com/range/'
|
||||||
(RED, GREEN, RESET) = ("\033[1;31m", "\033[0;32m", "\033[0;0m")
|
|
||||||
|
|
||||||
PASSWORD_API = 'https://api.pwnedpasswords.com/range/'
|
|
||||||
MAIL_API = 'https://haveibeenpwned.com/api/v2/breachedaccount/'
|
|
||||||
ROW = '{:<30}{:<10}{:<45}'
|
ROW = '{:<30}{:<10}{:<45}'
|
||||||
HIDDEN = True
|
HIDDEN = False
|
||||||
INFINITE = True
|
|
||||||
|
|
||||||
|
|
||||||
parser = ArgumentParser(
|
def header():
|
||||||
description='Check if your email or password appears in a data leak.',
|
print()
|
||||||
epilog='Moritz Münch, moritzmuench@mailbox.org, willipink.eu, GPL3+')
|
print(ROW.format('password', 'leaked', 'sha1'))
|
||||||
parser.add_argument('input',
|
print('-' * 80)
|
||||||
nargs='*',
|
|
||||||
metavar='password or mail adress',
|
|
||||||
help='''You can specify passwords and mail adresses seperated by spaces.
|
|
||||||
If the password contains a space you have to enclose it in quotes like ' or
|
|
||||||
"''')
|
|
||||||
parser.add_argument('-s', '-show', '--show',
|
|
||||||
nargs='?',
|
|
||||||
help='Show entered in leak summary')
|
|
||||||
args = parser.parse_args()
|
|
||||||
if args.show:
|
|
||||||
HIDDEN = False
|
|
||||||
|
|
||||||
|
|
||||||
def input_prompt():
|
def prompt_password():
|
||||||
|
print()
|
||||||
|
password = getpass('Tell me your password: ')
|
||||||
global HIDDEN
|
global HIDDEN
|
||||||
if HIDDEN:
|
HIDDEN = True
|
||||||
user_input = getpass('Tell me your password or mail adress: ')
|
header()
|
||||||
else:
|
query(password)
|
||||||
user_input = input('Tell me your password or mail adress: ')
|
|
||||||
check_mail_or_password(user_input)
|
|
||||||
|
|
||||||
|
|
||||||
def check_mail_or_password(user_input):
|
def query(password):
|
||||||
valid_mail = match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', user_input)
|
|
||||||
if valid_mail:
|
|
||||||
query_mail(valid_mail.group())
|
|
||||||
else:
|
|
||||||
query_password(user_input)
|
|
||||||
|
|
||||||
|
|
||||||
def no_connection_to(url):
|
|
||||||
''' Warn about no connection to *url* and exit '''
|
|
||||||
stdout.write(RED)
|
|
||||||
print('Could not connect to {}, please check your internet connection'.format(url))
|
|
||||||
stdout.write(RESET)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def query_password(password):
|
|
||||||
''' Query the first 5 digits of the sha1 hash of the user input
|
|
||||||
ti the haveibeenpwned.com api. '''
|
|
||||||
password_hash = sha1(password.encode('UTF-8')).hexdigest().upper()
|
password_hash = sha1(password.encode('UTF-8')).hexdigest().upper()
|
||||||
request = password_hash[:5]
|
request = password_hash[:5]
|
||||||
try:
|
response = get(API + request).text
|
||||||
response = requests.get(PASSWORD_API + request).text
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
no_connection_to(PASSWORD_API)
|
|
||||||
hash_searched = 'not yet'
|
hash_searched = 'not yet'
|
||||||
for answer in response.splitlines():
|
for answer in response.splitlines():
|
||||||
data = answer.split(':')
|
data = answer.split(':')
|
||||||
@ -105,53 +66,28 @@ def query_password(password):
|
|||||||
if password_hash == combined_hash:
|
if password_hash == combined_hash:
|
||||||
hash_searched = int(data[1])
|
hash_searched = int(data[1])
|
||||||
break
|
break
|
||||||
|
|
||||||
if hash_searched == 'not yet':
|
if hash_searched == 'not yet':
|
||||||
stdout.write(GREEN)
|
stdout.write(GREEN)
|
||||||
else:
|
else:
|
||||||
stdout.write(RED)
|
stdout.write(RED)
|
||||||
|
|
||||||
if HIDDEN:
|
if HIDDEN:
|
||||||
password = '*' * len(password)
|
password = '*' * len(password)
|
||||||
print(ROW.format('password', 'leaked', 'sha1'))
|
|
||||||
print('-' * 80)
|
|
||||||
print(ROW.format(password, hash_searched, password_hash))
|
print(ROW.format(password, hash_searched, password_hash))
|
||||||
stdout.write(RESET)
|
stdout.write(RESET)
|
||||||
if INFINITE:
|
|
||||||
input_prompt()
|
|
||||||
|
|
||||||
|
if HIDDEN:
|
||||||
def query_mail(mail):
|
prompt_password()
|
||||||
try:
|
|
||||||
response = requests.get(MAIL_API + mail).json()
|
|
||||||
for breach in response:
|
|
||||||
print('Name: {}'.format(breach['Name']))
|
|
||||||
print('Title: {}'.format(breach['Title']))
|
|
||||||
print('Domain: {}'.format(breach['Domain']))
|
|
||||||
print('Breach date: {}'.format(breach['BreachDate']))
|
|
||||||
print('Added date: {}'.format(breach['AddedDate']))
|
|
||||||
print('Modified date: {}'.format(breach['ModifiedDate']))
|
|
||||||
print('Pwn count: {}'.format(breach['PwnCount']))
|
|
||||||
print('Description: {}'.format(breach['Description']))
|
|
||||||
stdout.write(RED)
|
|
||||||
print('Breached data: {}'.format(', '.join([data for data in breach['DataClasses']])))
|
|
||||||
stdout.write(RESET)
|
|
||||||
print('Verified: {}'.format('yes' if breach['IsVerified'] == 1 else 'No'))
|
|
||||||
print('Fabricated: {}'.format('yes' if breach['IsFabricated'] == 1 else 'No'))
|
|
||||||
print('Sensitive: {}'.format('yes' if breach['IsSensitive'] == 1 else 'No'))
|
|
||||||
print('Retired: {}'.format('yes' if breach['IsRetired'] == 1 else 'No'))
|
|
||||||
print('Spam: {}'.format('yes' if breach['IsSpamList'] == 1 else 'No'))
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
no_connection_to(MAIL_API)
|
|
||||||
except:
|
|
||||||
stdout.write(GREEN)
|
|
||||||
print('The mail adress {} is not in the leak database.'.format(mail))
|
|
||||||
stdout.write(RESET)
|
|
||||||
query_password(mail)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if args.input:
|
if len(argv) < 2:
|
||||||
for user_input in args.input:
|
prompt_password()
|
||||||
check_mail_or_password(user_input)
|
|
||||||
else:
|
else:
|
||||||
INFINITE = True
|
header()
|
||||||
input_prompt()
|
for password in argv[1:]:
|
||||||
|
query(password)
|
||||||
|
print()
|
||||||
|
exit(0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user