Francois Lesueur
2 years ago
2 changed files with 381 additions and 0 deletions
@ -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)) |
@ -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=========") |
Loading…
Reference in new issue