|
import hashlib |
|
import random |
|
import subprocess |
|
|
|
import qrcode |
|
from PIL import Image, ImageOps |
|
|
|
|
|
def create_qr(data: str, box_size=10, border=4) -> Image: # returns in BnW |
|
qr = qrcode.QRCode( |
|
version=1, |
|
error_correction=qrcode.constants.ERROR_CORRECT_L, # About 7% or less errors can be corrected. |
|
box_size=box_size, |
|
border=border, |
|
) |
|
qr.add_data(data) |
|
qr.make(fit=True) |
|
return qr.make_image(fill_color="black", back_color="white").convert("L") |
|
|
|
|
|
def generate_qr_grid(password, wordlist_path, invert=False, verify_hashes=True) -> Image: # returns in BnW |
|
with open(wordlist_path, mode="rb") as f: |
|
password_qr = create_qr(data=password, box_size=1, border=2) |
|
passwords = f.readlines() |
|
hashes_with_coordinates = [] |
|
|
|
with open(".0sN1s.rule", mode="w", encoding="utf-8") as rule_: |
|
rule_.write("^ ^1\n^ ^0") |
|
|
|
for y in range(password_qr.height): |
|
for x in range(password_qr.width): |
|
|
|
while True: |
|
data_to_be_hashed = ( |
|
f"{password_qr.getpixel((x, y)) // 255} " |
|
+ random.choice(passwords)[:-1].decode() |
|
) |
|
if not verify_hashes: |
|
break |
|
crack_hashes_command = [ |
|
"hashcat", |
|
"-O", # Optimised Kernel |
|
"-m", # type of hash |
|
"1400", # SHA2-256 |
|
hashlib.sha256(data_to_be_hashed.encode()).hexdigest(), # hash to crack |
|
wordlist_path, # path to wordlist |
|
"-r", # use rule |
|
".0sN1s.rule", # path to rule |
|
] |
|
# crack the hashes using `hashcat` (make sure it's in your PATH) |
|
# see if the hash is actually crackable or not |
|
try: |
|
subprocess.run( |
|
crack_hashes_command, |
|
check=True, |
|
stdout=subprocess.DEVNULL, |
|
stderr=subprocess.DEVNULL, |
|
) |
|
break |
|
except subprocess.CalledProcessError: |
|
continue |
|
|
|
hashes_with_coordinates.append( |
|
f"({x}, {y}) = " |
|
+ hashlib.sha256(data_to_be_hashed.encode()).hexdigest() |
|
# + f":{data_to_be_hashed}" |
|
) |
|
print(f"Passed ({x}, {y})") |
|
|
|
__import__("os").remove(".0sN1s.rule") |
|
side = create_qr(data=hashes_with_coordinates[-1], box_size=1, border=2).width |
|
random.shuffle(hashes_with_coordinates) |
|
with Image.new( |
|
mode="L", |
|
color=255, |
|
size=(side * password_qr.width, side * password_qr.width), |
|
) as im: |
|
index = 0 |
|
for y in range(0, im.height, side): |
|
for x in range(0, im.width, side): |
|
im.paste( |
|
create_qr( |
|
data=hashes_with_coordinates[index], |
|
box_size=1, |
|
border=2, |
|
), |
|
(x, y), |
|
) |
|
index += 1 |
|
return ImageOps.invert(im) if invert else im |
|
|
|
|
|
def encrypt(password, src_img_path: str, wordlist_path: str, invert=False, verify_hashes=True) -> Image: |
|
# hides the qr code grid inside the red channel |
|
# of the source image |
|
qr_grid = generate_qr_grid( |
|
password=password, |
|
wordlist_path=wordlist_path, |
|
invert=invert, |
|
verify_hashes=verify_hashes, |
|
) |
|
src_img = Image.open(src_img_path).convert("RGB") |
|
|
|
if src_img.width < qr_grid.width or src_img.height < qr_grid.height: |
|
src_img = src_img.resize((qr_grid.width, qr_grid.height)) |
|
|
|
pixels = [] |
|
for y in range(src_img.height): |
|
for x in range(src_img.width): |
|
try: |
|
red_channel = int( |
|
bin(src_img.getpixel((x, y))[0])[:-1] |
|
+ str(qr_grid.getpixel((x, y)) // 255), |
|
base=2, |
|
) |
|
except IndexError: |
|
red_channel = src_img.getpixel((x, y))[0] |
|
|
|
pixels.append( |
|
( |
|
red_channel, |
|
src_img.getpixel((x, y))[1], |
|
src_img.getpixel((x, y))[2], |
|
) |
|
) |
|
src_img.putdata(pixels) |
|
return src_img |
|
|
|
|
|
if __name__ == "__main__": |
|
generate_qr_grid( |
|
password="<<<spam>>>", |
|
wordlist_path="../rockyou.txt", |
|
invert=True, |
|
verify_hashes=False, |
|
).save("qr_codes.png") |
|
|
|
# encrypt( |
|
# password="<<<spam>>>", |
|
# src_img_path="lol.png", |
|
# wordlist_path="rockyou.txt", |
|
# invert=True, |
|
# verify_hashes=False, |
|
# ).save("encr.png") |
|
|
|
## ~$ hashcat -O -m 1400 hashes.txt rockyou.txt -r 0sN1s.rule # Do this to crack the hashes effectively |
|
##0sN1s.rule: |
|
##^ ^1 |
|
##^ ^0 |
|
##well.. these rules exist in dive.rule, but that's just too many rules and this custom ruleset really speeds things up (duh) |