Autarch/web/routes/ad_audit.py
DigiJ cdde8717d0 v2.3.0 — RCS exploit v2.0, Starlink hack, SMS forge, Archon RCS module
Major RCS/SMS exploitation rewrite (v2.0):
- bugle_db direct extraction (plaintext messages, no decryption needed)
- CVE-2024-0044 run-as privilege escalation (Android 12-13)
- AOSP RCS provider queries (content://rcs/)
- Archon app relay for Shizuku-elevated bugle_db access
- 7-tab web UI: Extract, Database, Forge, Modify, Exploit, Backup, Monitor
- SQL query interface for extracted databases
- Full backup/restore/clone with SMS Backup & Restore XML support
- Known CVE database (CVE-2023-24033, CVE-2024-49415, CVE-2025-48593)
- IMS/RCS diagnostics, Phenotype verbose logging, Pixel tools

New modules: Starlink hack, SMS forge, SDR drone detection
Archon Android app: RCS messaging module with Shizuku integration
Updated manuals to v2.3, 60 web blueprints confirmed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 13:50:59 -08:00

191 lines
5.4 KiB
Python

"""Active Directory Audit routes."""
from flask import Blueprint, request, jsonify, render_template
from web.auth import login_required
ad_audit_bp = Blueprint('ad_audit', __name__, url_prefix='/ad-audit')
def _get_ad():
from modules.ad_audit import get_ad_audit
return get_ad_audit()
@ad_audit_bp.route('/')
@login_required
def index():
return render_template('ad_audit.html')
@ad_audit_bp.route('/connect', methods=['POST'])
@login_required
def connect():
data = request.get_json(silent=True) or {}
host = data.get('host', '').strip()
domain = data.get('domain', '').strip()
username = data.get('username', '').strip() or None
password = data.get('password', '') or None
use_ssl = bool(data.get('ssl', False))
if not host or not domain:
return jsonify({'success': False, 'message': 'DC host and domain are required'}), 400
return jsonify(_get_ad().connect(host, domain, username, password, use_ssl))
@ad_audit_bp.route('/disconnect', methods=['POST'])
@login_required
def disconnect():
return jsonify(_get_ad().disconnect())
@ad_audit_bp.route('/status')
@login_required
def status():
return jsonify(_get_ad().get_connection_info())
@ad_audit_bp.route('/users')
@login_required
def users():
search_filter = request.args.get('filter')
return jsonify(_get_ad().enumerate_users(search_filter))
@ad_audit_bp.route('/groups')
@login_required
def groups():
search_filter = request.args.get('filter')
return jsonify(_get_ad().enumerate_groups(search_filter))
@ad_audit_bp.route('/computers')
@login_required
def computers():
return jsonify(_get_ad().enumerate_computers())
@ad_audit_bp.route('/ous')
@login_required
def ous():
return jsonify(_get_ad().enumerate_ous())
@ad_audit_bp.route('/gpos')
@login_required
def gpos():
return jsonify(_get_ad().enumerate_gpos())
@ad_audit_bp.route('/trusts')
@login_required
def trusts():
return jsonify(_get_ad().enumerate_trusts())
@ad_audit_bp.route('/dcs')
@login_required
def dcs():
return jsonify(_get_ad().find_dcs())
@ad_audit_bp.route('/kerberoast', methods=['POST'])
@login_required
def kerberoast():
data = request.get_json(silent=True) or {}
ad = _get_ad()
host = data.get('host', '').strip() or ad.dc_host
domain = data.get('domain', '').strip() or ad.domain
username = data.get('username', '').strip() or ad.username
password = data.get('password', '') or ad.password
if not all([host, domain, username, password]):
return jsonify({'error': 'Host, domain, username, and password are required'}), 400
return jsonify(ad.kerberoast(host, domain, username, password))
@ad_audit_bp.route('/asrep', methods=['POST'])
@login_required
def asrep():
data = request.get_json(silent=True) or {}
ad = _get_ad()
host = data.get('host', '').strip() or ad.dc_host
domain = data.get('domain', '').strip() or ad.domain
userlist = data.get('userlist')
if isinstance(userlist, str):
userlist = [u.strip() for u in userlist.split(',') if u.strip()]
if not host or not domain:
return jsonify({'error': 'Host and domain are required'}), 400
return jsonify(ad.asrep_roast(host, domain, userlist or None))
@ad_audit_bp.route('/spray', methods=['POST'])
@login_required
def spray():
data = request.get_json(silent=True) or {}
ad = _get_ad()
userlist = data.get('userlist', [])
if isinstance(userlist, str):
userlist = [u.strip() for u in userlist.split('\n') if u.strip()]
password = data.get('password', '')
host = data.get('host', '').strip() or ad.dc_host
domain = data.get('domain', '').strip() or ad.domain
protocol = data.get('protocol', 'ldap')
if not userlist or not password or not host or not domain:
return jsonify({'error': 'User list, password, host, and domain are required'}), 400
return jsonify(ad.password_spray(userlist, password, host, domain, protocol))
@ad_audit_bp.route('/acls')
@login_required
def acls():
target_dn = request.args.get('target_dn')
return jsonify(_get_ad().analyze_acls(target_dn))
@ad_audit_bp.route('/admins')
@login_required
def admins():
return jsonify(_get_ad().find_admin_accounts())
@ad_audit_bp.route('/spn-accounts')
@login_required
def spn_accounts():
return jsonify(_get_ad().find_spn_accounts())
@ad_audit_bp.route('/asrep-accounts')
@login_required
def asrep_accounts():
return jsonify(_get_ad().find_asrep_accounts())
@ad_audit_bp.route('/unconstrained')
@login_required
def unconstrained():
return jsonify(_get_ad().find_unconstrained_delegation())
@ad_audit_bp.route('/constrained')
@login_required
def constrained():
return jsonify(_get_ad().find_constrained_delegation())
@ad_audit_bp.route('/bloodhound', methods=['POST'])
@login_required
def bloodhound():
data = request.get_json(silent=True) or {}
ad = _get_ad()
host = data.get('host', '').strip() or ad.dc_host
domain = data.get('domain', '').strip() or ad.domain
username = data.get('username', '').strip() or ad.username
password = data.get('password', '') or ad.password
if not all([host, domain, username, password]):
return jsonify({'error': 'Host, domain, username, and password are required'}), 400
return jsonify(ad.bloodhound_collect(host, domain, username, password))
@ad_audit_bp.route('/export')
@login_required
def export():
fmt = request.args.get('format', 'json')
return jsonify(_get_ad().export_results(fmt))