KernelSU module + Flipper Zero FAP that bridges both devices into a unified pentesting platform over USB CDC serial / BT rfcomm. Android side: bridge daemon, WebUI (:8089), bind mount namespace isolation stealth engine. Flipper side: proper FAP with 4-view GUI, GPIO/SubGHz/IR/file command handlers, async event streaming.
366 lines
12 KiB
Bash
366 lines
12 KiB
Bash
#!/system/bin/sh
|
|
# FlipperDroid WebUI — HTTP server with bridge API endpoints
|
|
|
|
WEBROOT="/data/adb/modules/flipperdroid/webroot"
|
|
CONFIG_DIR="/data/adb/flipperdroid"
|
|
CONFIG_FILE="$CONFIG_DIR/config.sh"
|
|
CMD_FIFO="$CONFIG_DIR/cmd_fifo"
|
|
RESP_DIR="$CONFIG_DIR/responses"
|
|
PORT=8089
|
|
|
|
mkdir -p "$CONFIG_DIR" "$RESP_DIR"
|
|
source "$CONFIG_FILE" 2>/dev/null
|
|
PORT="${WEBUI_PORT:-$PORT}"
|
|
|
|
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [webui] $1"; }
|
|
|
|
# Generate unique request ID
|
|
req_id() { echo "req_$(date +%s%N | cut -c1-16)_$$"; }
|
|
|
|
# Send command to bridge daemon via FIFO and wait for response
|
|
bridge_cmd() {
|
|
local subsystem="$1"
|
|
local action="$2"
|
|
local params="${3:-}"
|
|
local rid=$(req_id)
|
|
local timeout=10
|
|
|
|
echo "${rid}|${subsystem}|${action}|${params}" > "$CMD_FIFO" 2>/dev/null
|
|
|
|
# Wait for response file
|
|
local waited=0
|
|
while [ ! -f "$RESP_DIR/${rid}" ] && [ $waited -lt $timeout ]; do
|
|
sleep 0.1
|
|
waited=$((waited + 1))
|
|
done
|
|
|
|
if [ -f "$RESP_DIR/${rid}" ]; then
|
|
cat "$RESP_DIR/${rid}"
|
|
rm -f "$RESP_DIR/${rid}"
|
|
else
|
|
echo "ERR:timeout"
|
|
fi
|
|
}
|
|
|
|
# Read config as JSON
|
|
config_json() {
|
|
source "$CONFIG_FILE" 2>/dev/null
|
|
local status=$(cat "$CONFIG_DIR/status" 2>/dev/null || echo "unknown")
|
|
local conn_type=$(cat "$CONFIG_DIR/conn_type" 2>/dev/null || echo "none")
|
|
local flipper_dev=$(cat "$CONFIG_DIR/flipper_dev" 2>/dev/null || echo "")
|
|
local flipper_product=$(cat "$CONFIG_DIR/flipper_product" 2>/dev/null || echo "")
|
|
local flipper_serial=$(cat "$CONFIG_DIR/flipper_serial" 2>/dev/null || echo "")
|
|
local flipper_version=$(cat "$CONFIG_DIR/flipper_version" 2>/dev/null || echo "")
|
|
local flipper_caps=$(cat "$CONFIG_DIR/flipper_caps" 2>/dev/null || echo "")
|
|
|
|
cat << EOF
|
|
{
|
|
"status": "$status",
|
|
"conn_type": "$conn_type",
|
|
"conn_mode": "${CONN_MODE:-auto}",
|
|
"device": "$flipper_dev",
|
|
"product": "$flipper_product",
|
|
"serial": "$flipper_serial",
|
|
"firmware_version": "$flipper_version",
|
|
"capabilities": "$flipper_caps",
|
|
"config": {
|
|
"auto_connect": ${AUTO_CONNECT:-1},
|
|
"baud_rate": ${BAUD_RATE:-115200},
|
|
"webui_port": ${WEBUI_PORT:-8089},
|
|
"enable_gpio": ${ENABLE_GPIO:-1},
|
|
"enable_subghz": ${ENABLE_SUBGHZ:-1},
|
|
"enable_rfid": ${ENABLE_RFID:-1},
|
|
"enable_nfc": ${ENABLE_NFC:-1},
|
|
"enable_ir": ${ENABLE_IR:-1},
|
|
"enable_ibutton": ${ENABLE_IBUTTON:-1},
|
|
"enable_badusb": ${ENABLE_BADUSB:-0},
|
|
"cpu_share": ${CPU_SHARE:-1},
|
|
"cpu_share_threads": ${CPU_SHARE_THREADS:-2},
|
|
"log_level": ${LOG_LEVEL:-2}
|
|
}
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# Get recent events as JSON array
|
|
events_json() {
|
|
local count="${1:-20}"
|
|
local events_file="$CONFIG_DIR/events"
|
|
[ ! -f "$events_file" ] && echo "[]" && return
|
|
|
|
echo -n "["
|
|
local first=1
|
|
tail -n "$count" "$events_file" | while IFS='|' read -r type data1 data2 ts; do
|
|
[ $first -eq 1 ] && first=0 || echo -n ","
|
|
echo -n "{\"type\":\"$type\",\"data\":\"$data1\",\"extra\":\"$data2\",\"timestamp\":$ts}"
|
|
done
|
|
echo "]"
|
|
}
|
|
|
|
#############################
|
|
# HTTP request handler
|
|
#############################
|
|
|
|
handle_request() {
|
|
local method=""
|
|
local path=""
|
|
local content_length=0
|
|
local body=""
|
|
|
|
# Read request line
|
|
read -r line
|
|
method=$(echo "$line" | awk '{print $1}')
|
|
path=$(echo "$line" | awk '{print $2}')
|
|
|
|
# Read headers
|
|
while read -r header; do
|
|
header=$(echo "$header" | tr -d '\r')
|
|
[ -z "$header" ] && break
|
|
case "$header" in
|
|
Content-Length:*|content-length:*)
|
|
content_length=$(echo "$header" | awk '{print $2}')
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Read body if POST
|
|
if [ "$method" = "POST" ] && [ "$content_length" -gt 0 ] 2>/dev/null; then
|
|
body=$(dd bs=1 count="$content_length" 2>/dev/null)
|
|
fi
|
|
|
|
# Route
|
|
local response=""
|
|
local content_type="application/json"
|
|
local status="200 OK"
|
|
|
|
case "$method $path" in
|
|
# Static files
|
|
"GET /"|"GET /index.html")
|
|
content_type="text/html"
|
|
response=$(cat "$WEBROOT/index.html" 2>/dev/null)
|
|
;;
|
|
"GET /css/style.css")
|
|
content_type="text/css"
|
|
response=$(cat "$WEBROOT/css/style.css" 2>/dev/null)
|
|
;;
|
|
"GET /js/app.js")
|
|
content_type="application/javascript"
|
|
response=$(cat "$WEBROOT/js/app.js" 2>/dev/null)
|
|
;;
|
|
|
|
# API: Status & Config
|
|
"GET /api/status")
|
|
response=$(config_json)
|
|
;;
|
|
"GET /api/events"*)
|
|
response=$(events_json 50)
|
|
;;
|
|
"GET /api/log")
|
|
content_type="text/plain"
|
|
response=$(tail -n 100 "$CONFIG_DIR/logs/flipperdroid.log" 2>/dev/null)
|
|
;;
|
|
|
|
# API: System commands
|
|
"POST /api/system/ping")
|
|
response="{\"result\":\"$(bridge_cmd system ping)\"}"
|
|
;;
|
|
"POST /api/system/version")
|
|
response="{\"result\":\"$(bridge_cmd system version)\"}"
|
|
;;
|
|
"POST /api/system/status")
|
|
response="{\"result\":\"$(bridge_cmd system status)\"}"
|
|
;;
|
|
|
|
# API: GPIO
|
|
"POST /api/gpio/init")
|
|
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
|
local mode=$(echo "$body" | grep -o '"mode":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd gpio init "$pin,$mode")\"}"
|
|
;;
|
|
"POST /api/gpio/write")
|
|
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
|
local val=$(echo "$body" | grep -o '"value":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd gpio write "$pin,$val")\"}"
|
|
;;
|
|
"POST /api/gpio/read")
|
|
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd gpio read "$pin")\"}"
|
|
;;
|
|
"POST /api/gpio/pwm")
|
|
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
|
local freq=$(echo "$body" | grep -o '"freq":[0-9]*' | grep -o '[0-9]*')
|
|
local duty=$(echo "$body" | grep -o '"duty":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd gpio pwm "$pin,$freq,$duty")\"}"
|
|
;;
|
|
"POST /api/gpio/adc")
|
|
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd gpio adc "$pin")\"}"
|
|
;;
|
|
|
|
# API: SubGHz
|
|
"POST /api/subghz/set_freq")
|
|
local freq=$(echo "$body" | grep -o '"freq":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd subghz set_freq "$freq")\"}"
|
|
;;
|
|
"POST /api/subghz/tx")
|
|
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
|
|
response="{\"result\":\"$(bridge_cmd subghz tx "$data")\"}"
|
|
;;
|
|
"POST /api/subghz/rx_start")
|
|
response="{\"result\":\"$(bridge_cmd subghz rx_start)\"}"
|
|
;;
|
|
"POST /api/subghz/rx_stop")
|
|
response="{\"result\":\"$(bridge_cmd subghz rx_stop)\"}"
|
|
;;
|
|
"POST /api/subghz/get_rssi")
|
|
response="{\"result\":\"$(bridge_cmd subghz get_rssi)\"}"
|
|
;;
|
|
"POST /api/subghz/replay")
|
|
local slot=$(echo "$body" | grep -o '"slot":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd subghz replay "$slot")\"}"
|
|
;;
|
|
|
|
# API: RFID
|
|
"POST /api/rfid/read")
|
|
response="{\"result\":\"$(bridge_cmd rfid read)\"}"
|
|
;;
|
|
"POST /api/rfid/emulate")
|
|
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
|
|
response="{\"result\":\"$(bridge_cmd rfid emulate "$data")\"}"
|
|
;;
|
|
|
|
# API: NFC
|
|
"POST /api/nfc/poll")
|
|
response="{\"result\":\"$(bridge_cmd nfc poll)\"}"
|
|
;;
|
|
"POST /api/nfc/read_full")
|
|
response="{\"result\":\"$(bridge_cmd nfc read_full)\"}"
|
|
;;
|
|
"POST /api/nfc/emulate")
|
|
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
|
|
local type=$(echo "$body" | grep -o '"type":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd nfc emulate "${data}${type}")\"}"
|
|
;;
|
|
"POST /api/nfc/relay_start")
|
|
response="{\"result\":\"$(bridge_cmd nfc relay_start)\"}"
|
|
;;
|
|
"POST /api/nfc/relay_stop")
|
|
response="{\"result\":\"$(bridge_cmd nfc relay_stop)\"}"
|
|
;;
|
|
|
|
# API: IR
|
|
"POST /api/ir/tx")
|
|
local proto=$(echo "$body" | grep -o '"protocol":[0-9]*' | grep -o '[0-9]*')
|
|
local addr=$(echo "$body" | grep -o '"address":[0-9]*' | grep -o '[0-9]*')
|
|
local cmd=$(echo "$body" | grep -o '"command":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd ir tx "$proto,$addr,$cmd")\"}"
|
|
;;
|
|
"POST /api/ir/rx_start")
|
|
response="{\"result\":\"$(bridge_cmd ir rx_start)\"}"
|
|
;;
|
|
"POST /api/ir/rx_stop")
|
|
response="{\"result\":\"$(bridge_cmd ir rx_stop)\"}"
|
|
;;
|
|
"POST /api/ir/replay")
|
|
local slot=$(echo "$body" | grep -o '"slot":[0-9]*' | grep -o '[0-9]*')
|
|
response="{\"result\":\"$(bridge_cmd ir replay "$slot")\"}"
|
|
;;
|
|
|
|
# API: iButton
|
|
"POST /api/ibutton/read")
|
|
response="{\"result\":\"$(bridge_cmd ibutton read)\"}"
|
|
;;
|
|
"POST /api/ibutton/emulate")
|
|
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
|
|
response="{\"result\":\"$(bridge_cmd ibutton emulate "$data")\"}"
|
|
;;
|
|
|
|
# API: BadUSB
|
|
"POST /api/badusb/start")
|
|
response="{\"result\":\"$(bridge_cmd badusb start)\"}"
|
|
;;
|
|
"POST /api/badusb/exec")
|
|
local script=$(echo "$body" | grep -o '"script":"[^"]*"' | cut -d'"' -f4)
|
|
response="{\"result\":\"$(bridge_cmd badusb exec "$script")\"}"
|
|
;;
|
|
"POST /api/badusb/stop")
|
|
response="{\"result\":\"$(bridge_cmd badusb stop)\"}"
|
|
;;
|
|
|
|
# API: File operations on Flipper SD
|
|
"POST /api/file/list")
|
|
local fpath=$(echo "$body" | grep -o '"path":"[^"]*"' | cut -d'"' -f4)
|
|
response="{\"result\":\"$(bridge_cmd file list "$fpath")\"}"
|
|
;;
|
|
|
|
# API: Config update
|
|
"POST /api/config")
|
|
# Write config values back
|
|
local key=$(echo "$body" | grep -o '"key":"[^"]*"' | cut -d'"' -f4)
|
|
local value=$(echo "$body" | grep -o '"value":"[^"]*"' | cut -d'"' -f4)
|
|
if [ -n "$key" ] && [ -n "$value" ]; then
|
|
sed -i "s/^${key}=.*/${key}=${value}/" "$CONFIG_FILE"
|
|
source "$CONFIG_FILE"
|
|
response="{\"ok\":true}"
|
|
else
|
|
response="{\"ok\":false,\"error\":\"missing key or value\"}"
|
|
fi
|
|
;;
|
|
|
|
# API: Reconnect
|
|
"POST /api/reconnect")
|
|
echo "disconnected" > "$CONFIG_DIR/status"
|
|
response="{\"ok\":true,\"message\":\"reconnecting...\"}"
|
|
;;
|
|
|
|
# API: Stealth
|
|
"GET /api/stealth/status")
|
|
response=$(/system/bin/fd-stealth status 2>/dev/null || echo '{"error":"stealth not available"}')
|
|
;;
|
|
"POST /api/stealth/apply")
|
|
/system/bin/fd-stealth apply >> "$CONFIG_DIR/logs/stealth.log" 2>&1
|
|
response="{\"ok\":true,\"message\":\"stealth applied\"}"
|
|
;;
|
|
"POST /api/stealth/teardown")
|
|
/system/bin/fd-stealth teardown >> "$CONFIG_DIR/logs/stealth.log" 2>&1
|
|
response="{\"ok\":true,\"message\":\"stealth removed\"}"
|
|
;;
|
|
"POST /api/stealth/hide")
|
|
/system/bin/fd-stealth hide-dev >> "$CONFIG_DIR/logs/stealth.log" 2>&1
|
|
response="{\"ok\":true,\"message\":\"device hidden\"}"
|
|
;;
|
|
"POST /api/stealth/show")
|
|
/system/bin/fd-stealth show-dev >> "$CONFIG_DIR/logs/stealth.log" 2>&1
|
|
response="{\"ok\":true,\"message\":\"device visible\"}"
|
|
;;
|
|
|
|
*)
|
|
status="404 Not Found"
|
|
response="{\"error\":\"not found\"}"
|
|
;;
|
|
esac
|
|
|
|
# Send HTTP response
|
|
local content_length=${#response}
|
|
printf "HTTP/1.1 %s\r\n" "$status"
|
|
printf "Content-Type: %s\r\n" "$content_type"
|
|
printf "Content-Length: %d\r\n" "$content_length"
|
|
printf "Access-Control-Allow-Origin: *\r\n"
|
|
printf "Connection: close\r\n"
|
|
printf "\r\n"
|
|
printf "%s" "$response"
|
|
}
|
|
|
|
#############################
|
|
# Start HTTP server
|
|
#############################
|
|
|
|
log "FlipperDroid WebUI starting on port $PORT"
|
|
|
|
# Use busybox nc or toybox for HTTP serving
|
|
while true; do
|
|
handle_request | busybox nc -l -p "$PORT" -w 30 2>/dev/null || \
|
|
handle_request | toybox nc -l -p "$PORT" -W 30 2>/dev/null || \
|
|
handle_request | nc -l -p "$PORT" 2>/dev/null
|
|
done
|