From 5b8df75e08c998ca77b0ccf554c93860c8c3f9a0 Mon Sep 17 00:00:00 2001 From: Francois Lesueur Date: Mon, 16 May 2022 15:00:09 +0200 Subject: [PATCH] ajout code td passwords --- td3-code/skeleton.py | 131 +++++++++++++++++++++++ td3-code/toolbox.py | 250 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100755 td3-code/skeleton.py create mode 100755 td3-code/toolbox.py diff --git a/td3-code/skeleton.py b/td3-code/skeleton.py new file mode 100755 index 0000000..7797a4c --- /dev/null +++ b/td3-code/skeleton.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 + +import time +import sys +from toolbox import * + +# You should tweak these values during the work +nblogins = 10 # would be larger in real-life +nbpasswords = 1000000 # would be larger in real-life +nbiterations = 10 # 10000 is currently recommended, should be adapted to the usecase and changed with time (improvement of computation power), like a key size + + +############################################ +# Part of the script to edit # +############################################ + +# Hint : you can call decrypt(key,data) to decrypt data using key +def crackencrypted(database): + key = readfile("enckey")[0] + crackeddb = [] + for i in database: + # i[0] is the login, i[1] is the encrypted password + #... + crackeddb.append((i[0],i[1])) # second argument should contain cleartext password + return crackeddb + +# Hint : - genshahashes(passwords) returns all the hashes of passwords dictionary +# - getpassfromshahash(hashes, hash) returns the password which hashed as "hash" in hashes +def cracksha(database): + global nbpasswords + passwords = getPassDict(nbpasswords) # passwords contains a dictionary of passwords + #... + crackeddb = [] + for i in database: + # i[0] is the login, i[1] is the hashed password + #... + crackeddb.append((i[0],i[1])) # second argument should contain cleartext password + return crackeddb + +# Hint : salthash(password, salt) return the salted hash of password +def cracksaltedsha(database): + global nbpasswords + passwords = getPassDict(nbpasswords) + crackeddb = [] + for i in database: + # i[0] is the login, i[1] is the hashed password, i[2] is the salt + #... + crackeddb.append((i[0],i[1])) # second argument should contain cleartext password + return crackeddb + +# Hint : pbkdf2(password, salt, nbiterations) returns the pbkdf2 of password using salt and nbiterations +def crackpbkdf2(database): + global nbpasswords + passwords = getPassDict(nbpasswords) + crackeddb = [] + for i in database: + # i[0] is the login, i[1] is the hashed password, i[2] is the salt, i[3] is the iteration count + #... + crackeddb.append((i[0],i[1])) # second argument should contain cleartext password + return crackeddb + + + +############################################ +# Nothing to change after this line ! # +############################################ + + +if __name__ == '__main__': + # When called with init + if len(sys.argv) > 1 and sys.argv[1] == "init": + initworkspace(nblogins,nbpasswords,nbiterations) + print("Workspace initialized in files/ subdirectory") + exit(0) + + # Test whether init has been called before + try : + readfile("plain") + except FileNotFoundError: + initworkspace(nblogins,nbpasswords,nbiterations) + print("Workspace initialized in files/ subdirectory") + + # test plain DB + print("\n============\nPlain storage:") + plaindb = readfile("plain") + print("Plain DB is : " + str(plaindb)) + print("Authenticating with plain DB : " + str(authplain(plaindb[0][0],plaindb[0][1],plaindb))) + + #test encrypted db + print("\n============\nEncrypted storage:") + encdb = readfile("enc") + print("Encrypted DB is " + str(encdb)) + print("Authenticating with encrypted DB : " + str(authencrypted(plaindb[1][0],plaindb[1][1],encdb))) + start = time.time() + crackedenc = crackencrypted(encdb) + end = time.time() + print("Time to crack encrypted DB : " + str(end-start) + " seconds") + print("Cracked encrypted DB is " + str(crackedenc)) + + #test SHA db + print("\n============\nSHA storage:") + shadb = readfile("sha") + print("SHA DB is " + str(shadb)) + print("Authenticating with SHA DB : " + str(authsha(plaindb[0][0],plaindb[0][1],shadb))) + start = time.time() + crackedsha = cracksha(shadb) + end = time.time() + print("Time to crack SHA DB : " + str(end-start) + " seconds") + print("Cracked SHA DB is " + str(crackedsha)) + + #test Salted SHA db + print("\n============\nSalted SHA storage:") + saltedshadb = readfile("saltedsha") + print("Salted SHA DB is " + str(saltedshadb)) + print("Authenticating with Salted SHA DB : " + str(authsaltedsha(plaindb[0][0],plaindb[0][1],saltedshadb))) + start = time.time() + crackedsaltedsha = cracksaltedsha(saltedshadb) + end = time.time() + print("Time to crack salted SHA DB : " + str(end-start) + " seconds") + print("Cracked salted SHA DB is " + str(crackedsaltedsha)) + + # test PBKDF2 DB + print("\n============\nPBKDF2 storage:") + pbkdf2db = readfile("pbkdf2") + print("PBKDF2 DB is " + str(pbkdf2db)) + print("Authenticating with PBKDF2 DB : " + str(authpbkdf2(plaindb[0][0],plaindb[0][1],pbkdf2db))) + start = time.time() + crackedpbkdf2 = crackpbkdf2(pbkdf2db) + end = time.time() + print("Time to crack PBKDF2 DB : " + str(end-start) + " seconds") + print("Cracked PBKDF2 DB is " + str(crackedpbkdf2)) diff --git a/td3-code/toolbox.py b/td3-code/toolbox.py new file mode 100755 index 0000000..f931275 --- /dev/null +++ b/td3-code/toolbox.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 + +# Avoir une lib avec des briques prêtes +# Avoir un script qui crée les fichiers de password SHA/PB/etc. pour pouvoir les manipuler en texte +# TD : associer les briques pour évaluer les attaques sur un pass / une base +# mettre un « except ImportError » et ressayer avec « Cryptodome » a la place de « Crypto » + +import re +import time +import random +import hashlib +# tweak to (try to) handle different crypto lib naming across systems (Linux, Mac, Win) +try: + from Crypto.Cipher import AES + from Crypto import Random +except ImportError: + try: + from crypto.Cipher import AES + from crypto import Random + except ImportError: + from Cryptodome.Cipher import AES + from Cryptodome import Random +import base64 +import os +import urllib.request +import string + +# returns an array of a dictionary of passwords +def getPassDict(nbpasswords): + try: + f = open("files/passwords.txt") + except FileNotFoundError: + print("Downloading a passwords list...") + urllib.request.urlretrieve("https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/10-million-password-list-top-1000000.txt?raw=true", "files/passwords.txt") + print("Done !") + f = open("files/passwords.txt") + passwords = [] + #nbpasswords = 10000 + passtogen = nbpasswords + for password in f: + passwords.append(password.strip()) + passtogen-=1 + if passtogen == 0: + break + return passwords + +def genRandomPassword(): + length = 6 + chars = string.ascii_letters + string.digits + return ''.join(random.choice(chars) for i in range(length)) + +# reads/writes shadow-style files +def readfile(filename): + f = open("files/"+filename) + res = [] + for line in f: + output = line.strip().split(":") + res.append(output) + return res + +def writeFile(filename, array): + f = open("files/"+filename,'w') + for line in array: + towrite = "" + for item in line: + towrite+=item + ":" + towrite = towrite[:-1] + f.write(towrite) + f.write('\n') + + +# Plain storage +def genplain(nblogins,nbpasswords): + passwords = getPassDict(nbpasswords) + logins = [] + for i in range(0,nblogins): + login = "user" + str(i) + if (random.randint(0,10) < 4): + logins.append((login,passwords[random.randint(0,len(passwords)-1)])) + else: + logins.append((login,genRandomPassword())) + return logins + +def authplain(login, passwd, database): + for i in database: + if i[0] == login: + current = i[1] + return (current == passwd) + +# Encrypted storage +def genencrypted(logins): + encdb = [] + key = Random.new().read(16) + iv = Random.new().read(AES.block_size) + f = open("files/enckey",'wb') + f.write((base64.b64encode(key))) + f.write(b":") + #f = open("files/enciv",'wb') + f.write((base64.b64encode(iv))) + for i in logins: + cipher = AES.new(key, AES.MODE_CFB, iv) + enc = (base64.b64encode(cipher.encrypt(i[1].encode('utf-8')))).decode("utf-8") + encdb.append((i[0],enc)) + #print(enc) + return encdb + +def authencrypted(login, passwd, database): + for i in database: + if i[0] == login: + current = i[1] + keyiv = readfile("enckey")[0] + key = base64.b64decode(keyiv[0]) + iv = base64.b64decode(keyiv[1]) + #key = base64.b64decode(readfile("enckey")[0][0]) + #iv = base64.b64decode(readfile("enciv")[0][0]) + cipher = AES.new(key, AES.MODE_CFB, iv) + return (passwd == cipher.decrypt(base64.b64decode(current)).decode('utf-8')) + +def decrypt(keyiv,data): + key = base64.b64decode(keyiv[0]) + iv = base64.b64decode(keyiv[1]) + cipher = AES.new(key, AES.MODE_CFB, iv) + return cipher.decrypt(base64.b64decode(data)).decode('utf-8') + + +# SHA storage +def gensha(logins): + db = [] + for i in logins: + csum = hashlib.sha256(i[1].encode('utf-8')).hexdigest() + db.append((i[0],csum)) + return db + +def authsha(login, passwd, database): + for i in database: + if i[0] == login: + current = i[1] + return (current == hashlib.sha256(passwd.encode('utf-8')).hexdigest()) + +def genshahashes(passwords): + hashes = [] + for passwd in passwords: + hashes.append([hashlib.sha256(passwd.encode('utf-8')).hexdigest(),passwd]) + return hashes + +def getpassfromshahash(hashes, hash): + for j in hashes: + if j[0] == hash: + return j[1] + return None + +# Salted SHA storage +def gensaltedsha(logins): + db = [] + for i in logins: + salt = str(random.randint(0,65535)) + csum = hashlib.sha256((i[1]+salt).encode('utf-8')).hexdigest() + db.append((i[0],csum,salt)) + return db + +def authsaltedsha(login, passwd, database): + for i in database: + if i[0] == login: + current = i[1] + salt = i[2] + return (current == hashlib.sha256((passwd+salt).encode('utf-8')).hexdigest()) + +def salthash(password,salt): + return hashlib.sha256((password+str(salt)).encode('utf-8')).hexdigest() + +# PBKDF2 storage +def genpbkdf2(logins,nbiterations): + db = [] + for i in logins: + salt = str(random.randint(0,65535)) + csum = base64.b64encode(hashlib.pbkdf2_hmac('sha256',i[1].encode('utf-8'),str(salt).encode('utf-8'),nbiterations)).decode('utf-8') + db.append((i[0],csum,salt,str(nbiterations))) + return db + +def authpbkdf2(login, passwd, database): + for i in database: + if i[0] == login: + current = i[1] + salt = i[2] + nbiterations = int(i[3]) + return (base64.b64decode(current) == hashlib.pbkdf2_hmac('sha256',passwd.encode('utf-8'),str(salt).encode('utf-8'),nbiterations)) + +def pbkdf2(password,salt,nbiterations): + nbiterations = int(nbiterations) + return base64.b64encode(hashlib.pbkdf2_hmac('sha256',password.encode('utf-8'),str(salt).encode('utf-8'),nbiterations)).decode('utf-8') + + +# Generate shadow-style files +def initworkspace(nblogins,nbpasswords,nbiterations): + print("Generating " + str(nblogins) + " logins and " + str(nbpasswords) + " passwords") + try : + os.mkdir("files") + except FileExistsError: + pass + plaindb = genplain(nblogins,nbpasswords) + writeFile("plain", plaindb) + encdb = genencrypted(plaindb) + writeFile("enc", encdb) + shadb = gensha(plaindb) + writeFile("sha", shadb) + saltedshadb = gensaltedsha(plaindb) + writeFile("saltedsha", saltedshadb) + pbkdf2db = genpbkdf2(plaindb,nbiterations) + writeFile("pbkdf2", pbkdf2db) + + + +# Unit tests +if __name__ == '__main__': + # create shadow files + initworkspace(10,100,1000) + + print("======\nUnit tests of the toolbox, you must work in skeleton.py\n=========") + + # test plain DB + print("\n============\nPlain storage:") + plaindb = readfile("plain") + print("Plain DB is : " + str(plaindb)) + print("Authenticating with plain DB : " + str(authplain(plaindb[0][0],plaindb[0][1],plaindb))) + + #test encrypted db + print("\n============\nEncrypted storage:") + encdb = readfile("enc") + print("Encrypted DB is " + str(encdb)) + print("Authenticating with encrypted DB : " + str(authencrypted(plaindb[1][0],plaindb[1][1],encdb))) + + #test SHA db + print("\n============\nSHA storage:") + shadb = readfile("sha") + print("SHA DB is " + str(shadb)) + print("Authenticating with SHA DB : " + str(authsha(plaindb[0][0],plaindb[0][1],shadb))) + + #test Salted SHA db + print("\n============\nSalted SHA storage:") + saltedshadb = readfile("saltedsha") + print("Salted SHA DB is " + str(saltedshadb)) + print("Authenticating with Salted SHA DB : " + str(authsaltedsha(plaindb[0][0],plaindb[0][1],saltedshadb))) + + # test PBKDF2 DB + print("\n============\nPBKDF2 storage:") + pbkdf2db = readfile("pbkdf2") + print("PBKDF2 DB is " + str(pbkdf2db)) + print("Authenticating with PBKDF2 DB : " + str(authpbkdf2(plaindb[0][0],plaindb[0][1],pbkdf2db))) + + print("\n======\nUnit tests of the toolbox, you must work in skeleton.py\n=========")