| Título | AidanPark openclaw-android 0.4.0 Arbitrary Command Execution / Policy Bypass |
|---|
| Descrição | ## Vulnerability Title
Policy Bypass via Exposed JavaScriptInterface in WebView Command Bridge
## Affected Component
`android/app/src/main/java/com/openclaw/android/JsBridge.kt` and `CommandRunner.kt`
Repository: https://github.com/AidanPark/openclaw-android
## Summary
An attacker with JavaScript execution capabilities within the OpenClaw Android WebView can call the exposed `window.OpenClaw.runCommandAsync` native bridge method using an attacker-controlled shell command. The native bridge passes that command directly to `sh -c`, leading to unrestricted arbitrary command execution under the app's OpenClaw/Termux runtime context.
## Technical Details
The vulnerability occurs because the Android WebView bridge exposes a command execution method directly to JavaScript without any constraints. It forwards a caller-controlled command string to a system shell, crossing the security boundary between the untrusted WebView JavaScript and privileged native Kotlin code.
**Vulnerable Bridge Registration**
In `MainActivity.kt`, the JavaScript interface is registered and exposed to the WebView context under the name `OpenClaw`:
```kotlin
settings.javaScriptEnabled = true
// ...
addJavascriptInterface(jsBridge, "OpenClaw")
```
**Exposed Command Method**
In `JsBridge.kt`, the `@JavascriptInterface` exposes `runCommandAsync`, which directly accepts a `cmd` string from JavaScript and passes it to `CommandRunner.runStreaming`:
```kotlin
@JavascriptInterface
fun runCommandAsync(
callbackId: String,
cmd: String,
) {
// ...
CommandRunner.runStreaming(cmd, env, bootstrapManager.homeDir) { output ->
// ...
}
```
**Unrestricted Execution Sink**
In `CommandRunner.kt`, the application executes the unvalidated `command` string by passing it directly to `sh -c` via `ProcessBuilder`. This allows full shell metacharacter interpretation:
```kotlin
val shell = env["PREFIX"]?.let { "$it/bin/sh" } ?: "/system/bin/sh"
val pb = ProcessBuilder(shell, "-c", command)
// ...
val process = pb.start()
```
## Impact
This vulnerability allows attackers to:
- Execute arbitrary shell commands under the app's OpenClaw/Termux runtime context after obtaining WebView JavaScript execution.
- Read, modify, or delete files available to that runtime, including local configuration and CLI credential files.
- Download or run additional scripts, alter setup/update scripts, or persist malicious changes inside the app runtime.
## Proof of Concept
The PoC proves that WebView JavaScript can invoke the native bridge with a command string, and that shell metacharacters (such as `;` or `>`) are actively interpreted by `sh -c`.
```javascript
// Execute inside the OpenClaw Android WebView JavaScript context.
window.OpenClaw.runCommandAsync(
"poc",
"printf openclaw-poc > $HOME/openclaw-bridge-poc.txt"
)
// To demonstrate full shell interpretation via metacharacters:
window.OpenClaw.runCommandAsync(
"poc2",
"printf first; printf second > $HOME/openclaw-bridge-poc-2.txt"
)
```
*Observed Execution:* By checking the device filesystem, the file `$HOME/openclaw-bridge-poc-2.txt` is successfully created, confirming that the shell interpreted the semicolon and executed the injected secondary payload.
## Remediation
Remove or strictly restrict arbitrary command execution through the WebView bridge.
1. **Remove Arbitrary Shell Access**: Do not expose `runCommand` or `runCommandAsync` to WebView JavaScript.
2. **Allowlist Operations**: Expose only fixed, named native operations needed by the UI (e.g., `getGitVersion()`).
3. **Avoid `sh -c`**: If native commands must be run, use `ProcessBuilder` with an exact executable and an explicit argument list rather than passing caller-controlled strings to a shell.
Example safe implementation direction:
```kotlin
// Replace arbitrary command strings with fixed, named native operations.
@JavascriptInterface
fun getGitVersion(): String {
val env = EnvironmentBuilder.build(activity)
val result = CommandRunner.runFixed(
executable = "git",
args = listOf("--version"),
env = env,
workDir = bootstrapManager.homeDir,
)
return gson.toJson(result)
}
```
## References
- Vulnerable bridge registration: `https://github.com/AidanPark/openclaw-android/blob/cfb0740fc0961f1dd1c2a22ecf133eae443fa96f/android/app/src/main/java/com/openclaw/android/MainActivity.kt#L130-L140`
- Vulnerable bridge method: `https://github.com/AidanPark/openclaw-android/blob/cfb0740fc0961f1dd1c2a22ecf133eae443fa96f/android/app/src/main/java/com/openclaw/android/JsBridge.kt#L447-L468`
- Shell execution sink: `https://github.com/AidanPark/openclaw-android/blob/cfb0740fc0961f1dd1c2a22ecf133eae443fa96f/android/app/src/main/java/com/openclaw/android/CommandRunner.kt#L55-L77` |
|---|
| Fonte | ⚠️ https://github.com/AidanPark/openclaw-android/issues/136 |
|---|
| Utilizador | Dem00000 (UID 98563) |
|---|
| Submissão | 08/06/2026 16h40 (há 1 mês) |
|---|
| Moderação | 09/07/2026 07h43 (1 month later) |
|---|
| Estado | Aceite |
|---|
| Entrada VulDB | 377120 [AidanPark openclaw-android até 0.4.0 Android WebView Bridge JsBridge.kt Elevação de Privilégios] |
|---|
| Pontos | 20 |
|---|