diff --git a/README.md b/README.md index abb8ab3..8f05ff9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,26 @@ # checkpassword_py -Password Checker interface. \ No newline at end of file +Python/tkinter port of the C# `checkpassword` (SHA1Hash) WinForms app. Checks passwords against a local pwned-password API using SHA1 hashes. + +## Requirements + +- Python 3 +- tkinter (on Debian/Ubuntu: `sudo apt install python3-tk`) + +No other dependencies — stdlib only. + +## Run + +``` +python3 checkpassword.py +``` + +## Features + +- **Hash** — SHA1-hashes the entered string and queries `https://www.home.daprogs.net/pwn/indexapi.php`. Shows the hit count if the password is in the master list. +- **ZeroTrust** — sends only `sha5` (first 5 hash chars) plus 3 chars from a random position, querying `https://www.home.daprogs.net/pwn/zerotrust.php`. Reveals the full hash only if the server confirms a match. +- **Create Report** — reads `passList-tci.csv` (rows: `user;...;password;domain`), hashes each password, queries the API, and writes compromised entries to `userReport-tci.txt`. Run from the directory containing the CSV. + +## Note + +SHA1 is used only as a lookup key for the pwned-password service, not for authentication. diff --git a/checkpassword.py b/checkpassword.py new file mode 100644 index 0000000..3c0f7ae --- /dev/null +++ b/checkpassword.py @@ -0,0 +1,152 @@ +import hashlib +import random +import tkinter as tk +from tkinter import font as tkfont +from urllib.parse import urlencode +from urllib.request import urlopen + +URL_FULL = "https://www.home.daprogs.net/pwn/indexapi.php" +URL_ZERO = "https://www.home.daprogs.net/pwn/zerotrust.php" + + +def compute_sha1(text: str) -> str: + return hashlib.sha1(text.encode("utf-8")).hexdigest() + + +def fetch(url: str, params: dict) -> str: + try: + with urlopen(f"{url}?{urlencode(params)}") as resp: + return resp.read().decode("utf-8") + except Exception as e: + return f"Request exception: {e}" + + +class App: + def __init__(self, root: tk.Tk): + self.root = root + root.title("Password Checker") + mono = tkfont.Font(family="Consolas", size=10) + + tk.Label(root, text="Enter String").grid(row=0, column=0, columnspan=2, sticky="w") + self.txt_input = tk.Entry(root, font=mono, width=40) + self.txt_input.grid(row=1, column=0, sticky="we") + self.btn_hash = tk.Button(root, text="Hash", command=self.on_hash) + self.btn_hash.grid(row=1, column=1, sticky="we") + self.txt_output = tk.Entry(root, font=mono, width=40) + self.txt_output.grid(row=2, column=0, sticky="we") + self.btn_zero = tk.Button(root, text="ZeroTrust", command=self.on_zero_trust) + self.btn_zero.grid(row=2, column=1, sticky="we") + self.lbl_status = tk.Label(root, text="...") + self.lbl_status.grid(row=3, column=0, columnspan=2, sticky="w") + self.txt_result = tk.Entry(root, font=mono, width=50) + self.txt_result.grid(row=4, column=0, columnspan=2, sticky="we") + self.txt_desc = tk.Text(root, font=mono, width=50, height=12) + self.txt_desc.grid(row=5, column=0, columnspan=2, sticky="we") + self.btn_report = tk.Button(root, text="Create Report", command=self.on_create_report) + self.btn_report.grid(row=6, column=0, columnspan=2, sticky="we") + root.columnconfigure(0, weight=1) + root.bind("", lambda e: self.on_hash()) + self.txt_input.focus_set() + + def clear_fields(self): + self.txt_desc.delete("1.0", "end") + self.txt_output.delete(0, "end") + self.txt_result.delete(0, "end") + self.root_update() + + def select_all_input(self): + self.txt_input.focus_set() + self.txt_input.selection_range(0, "end") + + def root_update(self): + self.root.update() + + def on_hash(self): + self.clear_fields() + h = compute_sha1(self.txt_input.get()) + self.txt_output.insert(0, h) + self.lbl_status.config(text="web request...") + self.root_update() + ret = fetch(URL_FULL, {"sha1": h}) + self.lbl_status.config(text="response...") + self.root_update() + self.txt_result.insert(0, ret) + self.lbl_status.config(text="...") + self.root_update() + if "BAD" in ret: + count = ret.split(":")[1].replace("\r\n", "") + self.txt_desc.insert("1.0", + "This password is in the master list.\r\n" + f"It has been seen {count} times\r\n" + "Even a value of 1 is bad.\r\n" + "You need to change this password ASAP!\r\n") + self.select_all_input() + + def on_zero_trust(self): + self.clear_fields() + h = compute_sha1(self.txt_input.get()) + self.txt_output.insert(0, h) + sha5 = h[:5] + start = random.randrange(6, 36) + sha6 = h[start:start + 3] + r_hash = h[5:] + self.txt_output.delete(0, "end") + self.txt_output.insert(0, r_hash) + self.lbl_status.config(text="web request...") + self.root_update() + ret = fetch(URL_ZERO, {"sha5": sha5, "sha6": sha6}) + self.lbl_status.config(text="response...") + self.root_update() + self.lbl_status.config(text=f"request sent: {sha5}-{sha6}") + self.root_update() + if "BAD" in ret: + if r_hash.lower() in ret.lower(): + self.txt_result.insert(0, "BAD - Your password is listed") + desc = ret.replace("BAD:\r\n", "") + self.txt_desc.insert("1.0", desc) + pos = desc.find(r_hash.upper()) + if pos >= 0: + self.txt_desc.tag_add("sel", f"1.0+{pos}", f"1.0+{pos + 35}") + self.txt_desc.see(pos) + else: + self.txt_result.insert(0, "OK - Not in list") + else: + self.txt_result.insert(0, ret) + self.select_all_input() + + def on_create_report(self): + self.clear_fields() + with open("passList-tci.csv", encoding="utf-8", errors="replace") as f: + pass_list = f.read().split("\r\n") + total = len(pass_list) + count = 0 + with open("userReport-tci.txt", "w", encoding="utf-8") as out: + for item in pass_list: + if "," in item: + items = item.split(",") + email = items[0].split(";")[0] + "@" + items[2] + password = items[1] + if len(password) > 0: + h = compute_sha1(password) + self.txt_output.delete(0, "end") + self.txt_output.insert(0, h) + self.txt_result.delete(0, "end") + self.txt_result.insert(0, f"{count}/{total}") + self.lbl_status.config(text="web request...") + self.root_update() + ret = fetch(URL_FULL, {"sha1": h}) + self.lbl_status.config(text="response...") + self.root_update() + if "BAD" in ret: + out.write(f"{email},Password is hacked!({ret.split(':')[1].replace(chr(13) + chr(10), '')})\n") + count += 1 + self.txt_output.delete(0, "end") + self.txt_output.insert(0, "Complete.") + self.lbl_status.config(text="Complete.") + self.root_update() + + +if __name__ == "__main__": + root = tk.Tk() + App(root) + root.mainloop()