985 lines
30 KiB
Python
985 lines
30 KiB
Python
|
|
"""Android Exploitation routes - App extraction, recon, payloads, boot, root."""
|
||
|
|
|
||
|
|
import os
|
||
|
|
from flask import Blueprint, render_template, request, jsonify
|
||
|
|
from web.auth import login_required
|
||
|
|
|
||
|
|
android_exploit_bp = Blueprint('android_exploit', __name__, url_prefix='/android-exploit')
|
||
|
|
|
||
|
|
|
||
|
|
def _get_mgr():
|
||
|
|
from core.android_exploit import get_exploit_manager
|
||
|
|
return get_exploit_manager()
|
||
|
|
|
||
|
|
|
||
|
|
def _get_serial():
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
serial = data.get('serial', '').strip()
|
||
|
|
if not serial:
|
||
|
|
# Auto-detect if only one device connected
|
||
|
|
devices = _get_mgr().hw.adb_devices()
|
||
|
|
online = [d for d in devices if d.get('state') == 'device']
|
||
|
|
if len(online) == 1:
|
||
|
|
return online[0]['serial'], None
|
||
|
|
return None, jsonify({'error': 'No device serial provided (and multiple/no devices found)'})
|
||
|
|
return serial, None
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/')
|
||
|
|
@login_required
|
||
|
|
def index():
|
||
|
|
from core.hardware import get_hardware_manager
|
||
|
|
hw = get_hardware_manager()
|
||
|
|
status = hw.get_status()
|
||
|
|
return render_template('android_exploit.html', status=status)
|
||
|
|
|
||
|
|
|
||
|
|
# ── App Extraction ────────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/apps/list', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def apps_list():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
include_system = data.get('include_system', False)
|
||
|
|
return jsonify(_get_mgr().list_packages(serial, include_system=include_system))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/apps/pull-apk', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def apps_pull_apk():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
package = data.get('package', '').strip()
|
||
|
|
if not package:
|
||
|
|
return jsonify({'error': 'No package provided'})
|
||
|
|
return jsonify(_get_mgr().pull_apk(serial, package))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/apps/pull-data', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def apps_pull_data():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
package = data.get('package', '').strip()
|
||
|
|
if not package:
|
||
|
|
return jsonify({'error': 'No package provided'})
|
||
|
|
return jsonify(_get_mgr().pull_app_data(serial, package))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/apps/shared-prefs', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def apps_shared_prefs():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
package = data.get('package', '').strip()
|
||
|
|
if not package:
|
||
|
|
return jsonify({'error': 'No package provided'})
|
||
|
|
return jsonify(_get_mgr().extract_shared_prefs(serial, package))
|
||
|
|
|
||
|
|
|
||
|
|
# ── Device Recon ──────────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/recon/device-dump', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def recon_device_dump():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().full_device_dump(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/recon/accounts', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def recon_accounts():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().get_accounts(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/recon/wifi', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def recon_wifi():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().get_wifi_passwords(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/recon/calls', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def recon_calls():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
limit = int(data.get('limit', 200))
|
||
|
|
return jsonify(_get_mgr().extract_call_logs(serial, limit=limit))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/recon/sms', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def recon_sms():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
limit = int(data.get('limit', 200))
|
||
|
|
return jsonify(_get_mgr().extract_sms(serial, limit=limit))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/recon/contacts', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def recon_contacts():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().extract_contacts(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/recon/browser', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def recon_browser():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().extract_browser_history(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/recon/credentials', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def recon_credentials():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().extract_saved_credentials(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/recon/export', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def recon_export():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().export_recon_report(serial))
|
||
|
|
|
||
|
|
|
||
|
|
# ── Payload Deployment ────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/payload/deploy', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def payload_deploy():
|
||
|
|
serial = request.form.get('serial', '').strip()
|
||
|
|
if not serial:
|
||
|
|
return jsonify({'error': 'No serial provided'})
|
||
|
|
remote_path = request.form.get('remote_path', '/data/local/tmp/').strip()
|
||
|
|
f = request.files.get('file')
|
||
|
|
if not f:
|
||
|
|
return jsonify({'error': 'No file uploaded'})
|
||
|
|
from core.paths import get_uploads_dir
|
||
|
|
upload_path = str(get_uploads_dir() / f.filename)
|
||
|
|
f.save(upload_path)
|
||
|
|
return jsonify(_get_mgr().deploy_binary(serial, upload_path, remote_path))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/payload/execute', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def payload_execute():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
remote_path = data.get('remote_path', '').strip()
|
||
|
|
args = data.get('args', '')
|
||
|
|
background = data.get('background', True)
|
||
|
|
if not remote_path:
|
||
|
|
return jsonify({'error': 'No remote_path provided'})
|
||
|
|
return jsonify(_get_mgr().execute_payload(serial, remote_path, args=args, background=background))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/payload/reverse-shell', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def payload_reverse_shell():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
lhost = data.get('lhost', '').strip()
|
||
|
|
lport = data.get('lport', '')
|
||
|
|
method = data.get('method', 'nc').strip()
|
||
|
|
if not lhost or not lport:
|
||
|
|
return jsonify({'error': 'Missing lhost or lport'})
|
||
|
|
return jsonify(_get_mgr().setup_reverse_shell(serial, lhost, int(lport), method))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/payload/persistence', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def payload_persistence():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
method = data.get('method', 'init.d').strip()
|
||
|
|
return jsonify(_get_mgr().install_persistence(serial, method))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/payload/list', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def payload_list():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().list_running_payloads(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/payload/kill', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def payload_kill():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
pid = data.get('pid', '').strip()
|
||
|
|
if not pid:
|
||
|
|
return jsonify({'error': 'No PID provided'})
|
||
|
|
return jsonify(_get_mgr().kill_payload(serial, pid))
|
||
|
|
|
||
|
|
|
||
|
|
# ── Boot / Recovery ───────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/boot/info', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def boot_info():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().get_bootloader_info(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/boot/backup', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def boot_backup():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().backup_boot_image(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/boot/unlock', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def boot_unlock():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().unlock_bootloader(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/boot/flash-recovery', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def boot_flash_recovery():
|
||
|
|
serial = request.form.get('serial', '').strip()
|
||
|
|
if not serial:
|
||
|
|
return jsonify({'error': 'No serial provided'})
|
||
|
|
f = request.files.get('file')
|
||
|
|
if not f:
|
||
|
|
return jsonify({'error': 'No file uploaded'})
|
||
|
|
from core.paths import get_uploads_dir
|
||
|
|
upload_path = str(get_uploads_dir() / f.filename)
|
||
|
|
f.save(upload_path)
|
||
|
|
return jsonify(_get_mgr().flash_recovery(serial, upload_path))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/boot/flash-boot', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def boot_flash_boot():
|
||
|
|
serial = request.form.get('serial', '').strip()
|
||
|
|
if not serial:
|
||
|
|
return jsonify({'error': 'No serial provided'})
|
||
|
|
f = request.files.get('file')
|
||
|
|
if not f:
|
||
|
|
return jsonify({'error': 'No file uploaded'})
|
||
|
|
from core.paths import get_uploads_dir
|
||
|
|
upload_path = str(get_uploads_dir() / f.filename)
|
||
|
|
f.save(upload_path)
|
||
|
|
return jsonify(_get_mgr().flash_boot(serial, upload_path))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/boot/disable-verity', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def boot_disable_verity():
|
||
|
|
# Supports both JSON and form upload
|
||
|
|
if request.content_type and 'multipart' in request.content_type:
|
||
|
|
serial = request.form.get('serial', '').strip()
|
||
|
|
f = request.files.get('file')
|
||
|
|
vbmeta = None
|
||
|
|
if f:
|
||
|
|
from core.paths import get_uploads_dir
|
||
|
|
vbmeta = str(get_uploads_dir() / f.filename)
|
||
|
|
f.save(vbmeta)
|
||
|
|
else:
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
serial = data.get('serial', '').strip()
|
||
|
|
vbmeta = None
|
||
|
|
if not serial:
|
||
|
|
return jsonify({'error': 'No serial provided'})
|
||
|
|
return jsonify(_get_mgr().disable_verity(serial, vbmeta))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/boot/temp-boot', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def boot_temp():
|
||
|
|
serial = request.form.get('serial', '').strip()
|
||
|
|
if not serial:
|
||
|
|
return jsonify({'error': 'No serial provided'})
|
||
|
|
f = request.files.get('file')
|
||
|
|
if not f:
|
||
|
|
return jsonify({'error': 'No file uploaded'})
|
||
|
|
from core.paths import get_uploads_dir
|
||
|
|
upload_path = str(get_uploads_dir() / f.filename)
|
||
|
|
f.save(upload_path)
|
||
|
|
return jsonify(_get_mgr().boot_temp(serial, upload_path))
|
||
|
|
|
||
|
|
|
||
|
|
# ── Root Methods ──────────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/root/check', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def root_check():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().check_root(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/root/install-magisk', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def root_install_magisk():
|
||
|
|
serial = request.form.get('serial', '').strip()
|
||
|
|
if not serial:
|
||
|
|
return jsonify({'error': 'No serial provided'})
|
||
|
|
f = request.files.get('file')
|
||
|
|
if not f:
|
||
|
|
return jsonify({'error': 'No file uploaded'})
|
||
|
|
from core.paths import get_uploads_dir
|
||
|
|
upload_path = str(get_uploads_dir() / f.filename)
|
||
|
|
f.save(upload_path)
|
||
|
|
return jsonify(_get_mgr().install_magisk(serial, upload_path))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/root/pull-patched', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def root_pull_patched():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().pull_patched_boot(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/root/exploit', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def root_exploit():
|
||
|
|
serial = request.form.get('serial', '').strip()
|
||
|
|
if not serial:
|
||
|
|
return jsonify({'error': 'No serial provided'})
|
||
|
|
f = request.files.get('file')
|
||
|
|
if not f:
|
||
|
|
return jsonify({'error': 'No file uploaded'})
|
||
|
|
from core.paths import get_uploads_dir
|
||
|
|
upload_path = str(get_uploads_dir() / f.filename)
|
||
|
|
f.save(upload_path)
|
||
|
|
return jsonify(_get_mgr().root_via_exploit(serial, upload_path))
|
||
|
|
|
||
|
|
|
||
|
|
# ── SMS Manipulation ─────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/sms/list', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def sms_list():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
limit = int(data.get('limit', 50))
|
||
|
|
address = data.get('address', '').strip() or None
|
||
|
|
return jsonify(_get_mgr().sms_list(serial, limit=limit, address=address))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/sms/insert', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def sms_insert():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
address = data.get('address', '').strip()
|
||
|
|
body = data.get('body', '').strip()
|
||
|
|
if not address or not body:
|
||
|
|
return jsonify({'error': 'Missing address or body'})
|
||
|
|
return jsonify(_get_mgr().sms_insert(
|
||
|
|
serial, address, body,
|
||
|
|
date_str=data.get('date') or None,
|
||
|
|
time_str=data.get('time') or None,
|
||
|
|
msg_type=data.get('type', 'inbox'),
|
||
|
|
read=data.get('read', True),
|
||
|
|
))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/sms/bulk-insert', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def sms_bulk_insert():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
messages = data.get('messages', [])
|
||
|
|
if not messages:
|
||
|
|
return jsonify({'error': 'No messages provided'})
|
||
|
|
return jsonify(_get_mgr().sms_bulk_insert(serial, messages))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/sms/update', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def sms_update():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
sms_id = data.get('id', '').strip()
|
||
|
|
if not sms_id:
|
||
|
|
return jsonify({'error': 'No SMS id provided'})
|
||
|
|
return jsonify(_get_mgr().sms_update(
|
||
|
|
serial, sms_id,
|
||
|
|
body=data.get('body'),
|
||
|
|
date_str=data.get('date'),
|
||
|
|
time_str=data.get('time'),
|
||
|
|
address=data.get('address'),
|
||
|
|
msg_type=data.get('type'),
|
||
|
|
read=data.get('read'),
|
||
|
|
))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/sms/delete', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def sms_delete():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
sms_id = data.get('id', '').strip() if data.get('id') else None
|
||
|
|
address = data.get('address', '').strip() if data.get('address') else None
|
||
|
|
delete_all_from = data.get('delete_all_from', False)
|
||
|
|
return jsonify(_get_mgr().sms_delete(serial, sms_id=sms_id, address=address,
|
||
|
|
delete_all_from=delete_all_from))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/sms/delete-all', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def sms_delete_all():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().sms_delete_all(serial))
|
||
|
|
|
||
|
|
|
||
|
|
# ── RCS Spoofing ─────────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/rcs/check', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def rcs_check():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().rcs_check_support(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/rcs/list', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def rcs_list():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
limit = int(data.get('limit', 50))
|
||
|
|
return jsonify(_get_mgr().rcs_list(serial, limit=limit))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/rcs/insert', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def rcs_insert():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
address = data.get('address', '').strip()
|
||
|
|
body = data.get('body', '').strip()
|
||
|
|
if not address or not body:
|
||
|
|
return jsonify({'error': 'Missing address or body'})
|
||
|
|
return jsonify(_get_mgr().rcs_insert(
|
||
|
|
serial, address, body,
|
||
|
|
date_str=data.get('date') or None,
|
||
|
|
time_str=data.get('time') or None,
|
||
|
|
sender_name=data.get('sender_name') or None,
|
||
|
|
is_outgoing=data.get('is_outgoing', False),
|
||
|
|
))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/rcs/delete', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def rcs_delete():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
msg_id = data.get('id', '')
|
||
|
|
if not msg_id:
|
||
|
|
return jsonify({'error': 'No message id provided'})
|
||
|
|
return jsonify(_get_mgr().rcs_delete(serial, int(msg_id)))
|
||
|
|
|
||
|
|
|
||
|
|
# ── Screen & Input ───────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/capture', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def screen_capture():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().screen_capture(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/record', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def screen_record():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
duration = int(data.get('duration', 10))
|
||
|
|
return jsonify(_get_mgr().screen_record(serial, duration=duration))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/tap', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def screen_tap():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().input_tap(serial, data.get('x', 0), data.get('y', 0)))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/swipe', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def screen_swipe():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().input_swipe(serial, data.get('x1',0), data.get('y1',0),
|
||
|
|
data.get('x2',0), data.get('y2',0),
|
||
|
|
int(data.get('duration', 300))))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/text', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def screen_text():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
text = data.get('text', '')
|
||
|
|
if not text:
|
||
|
|
return jsonify({'error': 'No text provided'})
|
||
|
|
return jsonify(_get_mgr().input_text(serial, text))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/key', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def screen_key():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().input_keyevent(serial, data.get('keycode', 3)))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/keylogger-start', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def keylogger_start():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().start_keylogger(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/keylogger-stop', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def keylogger_stop():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().stop_keylogger(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/dismiss-lock', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def dismiss_lock():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().dismiss_lockscreen(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/screen/disable-lock', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def disable_lock():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().disable_lockscreen(serial))
|
||
|
|
|
||
|
|
|
||
|
|
# ── Advanced: Data ───────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/clipboard', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_clipboard():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().extract_clipboard(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/notifications', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_notifications():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().dump_notifications(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/location', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_location():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().extract_location(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/media-list', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_media_list():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().extract_media_list(serial, media_type=data.get('type', 'photos')))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/media-pull', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_media_pull():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().pull_media_folder(serial, media_type=data.get('type', 'photos'),
|
||
|
|
limit=int(data.get('limit', 50))))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/whatsapp', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_whatsapp():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().extract_whatsapp_db(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/telegram', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_telegram():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().extract_telegram_db(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/signal', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_signal():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().extract_signal_db(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/fingerprint', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_fingerprint():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().get_device_fingerprint(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/settings', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_settings():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().dump_all_settings(serial))
|
||
|
|
|
||
|
|
|
||
|
|
# ── Advanced: Network ────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/network-info', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_network_info():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().get_network_info(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/proxy-set', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_proxy_set():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
host = data.get('host', '').strip()
|
||
|
|
port = data.get('port', '').strip()
|
||
|
|
if not host or not port:
|
||
|
|
return jsonify({'error': 'Missing host or port'})
|
||
|
|
return jsonify(_get_mgr().set_proxy(serial, host, port))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/proxy-clear', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_proxy_clear():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().clear_proxy(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/wifi-scan', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_wifi_scan():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().wifi_scan(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/wifi-connect', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_wifi_connect():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().wifi_connect(serial, data.get('ssid',''), data.get('password','')))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/adb-wifi', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_adb_wifi():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().enable_adb_wifi(serial, int(data.get('port', 5555))))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/capture-traffic', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_capture():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().capture_traffic(serial,
|
||
|
|
interface=data.get('interface', 'any'),
|
||
|
|
duration=int(data.get('duration', 30)),
|
||
|
|
pcap_filter=data.get('filter', '')))
|
||
|
|
|
||
|
|
|
||
|
|
# ── Advanced: System ─────────────────────────────────────────────
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/selinux', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_selinux():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().set_selinux(serial, data.get('mode', 'permissive')))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/remount', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_remount():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().remount_system(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/logcat-sensitive', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_logcat():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
return jsonify(_get_mgr().logcat_sensitive(serial, int(data.get('duration', 10))))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/processes', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_processes():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().get_running_processes(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/ports', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_ports():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
return jsonify(_get_mgr().get_open_ports(serial))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/modify-setting', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_modify_setting():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
ns = data.get('namespace', '').strip()
|
||
|
|
key = data.get('key', '').strip()
|
||
|
|
value = data.get('value', '').strip()
|
||
|
|
if not ns or not key:
|
||
|
|
return jsonify({'error': 'Missing namespace or key'})
|
||
|
|
return jsonify(_get_mgr().modify_setting(serial, ns, key, value))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/app-disable', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_app_disable():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
package = data.get('package', '').strip()
|
||
|
|
if not package:
|
||
|
|
return jsonify({'error': 'No package provided'})
|
||
|
|
return jsonify(_get_mgr().disable_app(serial, package))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/app-enable', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_app_enable():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
package = data.get('package', '').strip()
|
||
|
|
if not package:
|
||
|
|
return jsonify({'error': 'No package provided'})
|
||
|
|
return jsonify(_get_mgr().enable_app(serial, package))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/app-clear', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_app_clear():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
package = data.get('package', '').strip()
|
||
|
|
if not package:
|
||
|
|
return jsonify({'error': 'No package provided'})
|
||
|
|
return jsonify(_get_mgr().clear_app_data(serial, package))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/app-launch', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_app_launch():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
package = data.get('package', '').strip()
|
||
|
|
if not package:
|
||
|
|
return jsonify({'error': 'No package provided'})
|
||
|
|
return jsonify(_get_mgr().launch_app(serial, package))
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/adv/content-query', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def adv_content_query():
|
||
|
|
serial, err = _get_serial()
|
||
|
|
if err:
|
||
|
|
return err
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
uri = data.get('uri', '').strip()
|
||
|
|
if not uri:
|
||
|
|
return jsonify({'error': 'No URI provided'})
|
||
|
|
return jsonify(_get_mgr().content_query(serial, uri,
|
||
|
|
projection=data.get('projection', ''), where=data.get('where', '')))
|
||
|
|
|
||
|
|
|
||
|
|
# ── WebUSB Direct Mode: Command Relay ────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/cmd', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def get_direct_commands():
|
||
|
|
"""Return ADB shell command(s) for an operation without executing them.
|
||
|
|
Used by WebUSB Direct mode: browser executes via HWDirect.adbShell().
|
||
|
|
Returns one of:
|
||
|
|
{commands: ['cmd1', 'cmd2', ...]} — shell operations
|
||
|
|
{pullPath: '/device/path'} — file pull operations
|
||
|
|
{error: 'message'} — unsupported operation
|
||
|
|
"""
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
op = data.get('op', '')
|
||
|
|
params = data.get('params', {})
|
||
|
|
result = _get_mgr().get_commands_for_op(op, params)
|
||
|
|
return jsonify(result)
|
||
|
|
|
||
|
|
|
||
|
|
@android_exploit_bp.route('/parse', methods=['POST'])
|
||
|
|
@login_required
|
||
|
|
def parse_direct_output():
|
||
|
|
"""Parse raw ADB shell output from WebUSB Direct mode execution.
|
||
|
|
Takes the raw text output and returns the same structured JSON
|
||
|
|
that the normal server-mode endpoint would return.
|
||
|
|
"""
|
||
|
|
data = request.get_json(silent=True) or {}
|
||
|
|
op = data.get('op', '')
|
||
|
|
params = data.get('params', {})
|
||
|
|
raw = data.get('raw', '')
|
||
|
|
result = _get_mgr().parse_op_output(op, params, raw)
|
||
|
|
return jsonify(result)
|