a2aproject / a2aproject/a2a-samples

A2A Samples .NET CLI Agent allowlist bypass enables shell command injection

Đang mở
#636 1 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Jupyter Notebook
Star
1.8k
Fork
751
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

## Summary

The `samples/dotnet/A2ACliDemo/CLIServer` sample upgrades remote A2A `TextPart`
content into a local shell command. It attempts to protect execution with an
allowlist, but the allowlist only checks the first token returned by
`ParseCommand(input)`.

On Unix-like systems the remaining unvalidated argument string is interpolated
into:

```text
/bin/bash -c "{command} {arguments}"
```

An attacker can use an allowed first token such as `echo` and place shell
metacharacters in the argument string. Example:

```text
echo a2a-safe; printf A2A_PWNED_7f4d7c
```

`ParseCommand` treats `echo` as the command, `IsCommandAllowed("echo")` accepts
it, and `/bin/bash -c` executes the trailing `printf` command.

## Affected Target

- Repository: `a2aproject/a2a-samples`
- Commit tested: `d536d049451864995df9b5bb0b4f2e2d3cf65274`
- Component: `samples/dotnet/A2ACliDemo/CLIServer/CLIAgent.cs`

## Security Impact

If the sample CLI agent is exposed to untrusted A2A clients, a remote client can
execute shell metacharacters after any allowlisted command token. This bypasses
the sample's stated command allowlist boundary and can become remote command
execution in deployments that reuse the sample.

This is a sample issue rather than a core SDK issue, but it is security-relevant
because the sample explicitly demonstrates a bridge from A2A messages to
system-level operations.

The repository README also warns that data received from external agents,
including messages and artifacts, should be treated as untrusted input. This
issue is a concrete example of that boundary being crossed: untrusted A2A
message text is interpreted as a shell command line.

The configured allowlist also includes interpreter and package-manager style
commands such as `python`, `node`, `npm`, and `dotnet`. Even if shell
metacharacter injection were fixed, these entries should be treated carefully
because they can execute attacker-provided program text or project scripts when
arguments remain remotely controlled.

## Root Cause

The code extracts message text from the A2A request:

```text
var userText = GetTextFromMessage(messageSendParams.Message);
```

It parses only the first whitespace-delimited token as the command:

```text
var parts = ParseCommand(input);
var command = parts.Command;
var arguments = parts.Arguments;
```

It allowlists only `command`:

```text
if (!IsCommandAllowed(command)) { ... }
```

But then passes both `command` and attacker-controlled `arguments` through a
shell:

```text
process.StartInfo.Arguments = $"-c \"{command} {arguments}\"";
```

The guard and sink do not protect the same semantic object.

## Steps to reproduce

From the extracted package directory:

```bash
cd attachments
bash reproduce.sh
```

The included PoC is source-anchored and does not require the .NET SDK. It checks
the vulnerable anchors in `CLIAgent.cs`, replays the sample's parse and allowlist
logic, and executes the resulting benign shell command locally to prove that the
trailing marker command runs.

The script clones the tested `a2a-samples` commit if no local checkout is
provided. To run against an existing checkout:

```bash
cd attachments
A2A_SAMPLES_TARGET_DIR=/path/to/a2a-samples bash reproduce.sh
```

Expected result:

```json
{
"verdict": "fail",
"first_token_allowed": true,
"marker_observed": true
}
```

## Suggested Fix

Avoid shell interpretation for user-controlled message text. Prefer one of:

- dispatch each allowed command to a fixed handler;
- invoke a fixed executable with an argument array and no shell;
- validate the complete command grammar, including arguments, before execution;
- remove interpreters and package managers such as `python`, `node`, `npm`, and
`dotnet` from the default allowlist unless their arguments are separately
constrained by command-specific parsers;
- remove shell-backed execution from the public sample or mark it as trusted
local-only.

**Attachments**

[attachments.zip](https://github.com/user-attachments/files/29841022/attachments.zip)

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Lỗ hổng nằm trong samples/dotnet/A2ACliDemo/CLIServer/CLIAgent.cs. Hãy bắt đầu bằng việc kiểm tra ParseCommand, IsCommandAllowed và logic xây dựng lệnh shell. Chạy script reproduce.sh được cung cấp để hiểu exploit. Bản sửa bao gồm việc loại bỏ nội suy shell, xác thực toàn bộ chuỗi đối số hoặc chuyển sang dispatch executable cố định. Kiểm tra allowlist hiện có để tìm các mục nguy hiểm như python hoặc node.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
csharp, shell
Lĩnh vực
cli, security
Loại issue
Lỗi
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
45/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.