65 lines
2.9 KiB
Python
Executable File
65 lines
2.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Bulk password change for valid O365 accounts via ROPC"""
|
|
import requests,psycopg2,random,string,json,time,sys
|
|
from datetime import datetime
|
|
|
|
DB={"host":"localhost","dbname":"adx_system","user":"admin","password":"admin123"}
|
|
LOG="/opt/wevads/logs/bulk_pwd_change.log"
|
|
BATCH=int(sys.argv[1]) if len(sys.argv)>1 else 10
|
|
|
|
def log(m):
|
|
t=datetime.now().strftime("%H:%M:%S")
|
|
print(f"[{t}] {m}",flush=True)
|
|
with open(LOG,"a") as f: f.write(f"[{t}] {m}\n")
|
|
|
|
def gen():
|
|
while True:
|
|
p=''.join(random.choice(string.ascii_letters+string.digits+"!@#$") for _ in range(16))
|
|
if any(c.isupper() for c in p) and any(c.islower() for c in p) and any(c.isdigit() for c in p) and any(c in "!@#$" for c in p): return p
|
|
|
|
db=psycopg2.connect(**DB); db.autocommit=True; cur=db.cursor()
|
|
cur.execute("SELECT id,admin_email,admin_password,tenant_domain FROM admin.office_accounts WHERE password_status='valid' ORDER BY id LIMIT %s",[BATCH])
|
|
rows=cur.fetchall()
|
|
log(f"Processing {len(rows)} accounts")
|
|
ok=fail=skip=0
|
|
|
|
for aid,email,pwd,tenant in rows:
|
|
try:
|
|
r=requests.post(f"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token",data={
|
|
"grant_type":"password","client_id":"1b730954-1685-4b74-9bfd-dac224a7b894",
|
|
"scope":"https://graph.microsoft.com/.default","username":email,"password":pwd},timeout=20)
|
|
if r.status_code!=200:
|
|
err=r.json().get("error_description","")[:80]
|
|
if "50079" in err: status="mfa_required"
|
|
elif "50076" in err: status="mfa_required"
|
|
elif "50053" in err: status="locked"
|
|
elif "50057" in err: status="disabled"
|
|
elif "90002" in err: status="tenant_gone"
|
|
elif "50126" in err: status="invalid"
|
|
else: status="error"
|
|
cur.execute("UPDATE admin.office_accounts SET password_status=%s WHERE id=%s",[status,aid])
|
|
log(f"#{aid} {email}: {status} - {err[:50]}")
|
|
skip+=1; continue
|
|
|
|
token=r.json()["access_token"]
|
|
npwd=gen()
|
|
cp=requests.post("https://graph.microsoft.com/v1.0/me/changePassword",
|
|
json={"currentPassword":pwd,"newPassword":npwd},
|
|
headers={"Authorization":f"Bearer {token}","Content-Type":"application/json"},timeout=20)
|
|
|
|
if cp.status_code in [200,204]:
|
|
cur.execute("UPDATE admin.office_accounts SET admin_password=%s, password_status='pwd_changed', notes=%s WHERE id=%s",
|
|
[npwd, json.dumps({"old_pwd":pwd,"changed":datetime.now().isoformat(),"by":"bulk_pwd_change"}), aid])
|
|
log(f"#{aid} {email}: PWD CHANGED -> {npwd[:8]}...")
|
|
ok+=1
|
|
else:
|
|
log(f"#{aid} {email}: change fail {cp.status_code} {cp.text[:80]}")
|
|
fail+=1
|
|
except Exception as e:
|
|
log(f"#{aid} {email}: ERROR {e}")
|
|
fail+=1
|
|
time.sleep(0.5)
|
|
|
|
log(f"DONE: {ok} changed, {fail} failed, {skip} skipped")
|
|
db.close()
|