Back to skill

Security audit

Ews Email

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims for Exchange email, but its attachment download code can write outside the chosen folder and its headless Linux credential guidance weakens protection of mailbox credentials.

Install only if you trust this skill with full access to the configured Exchange mailbox and are comfortable reviewing its code. Avoid using attachment-download until filenames are sanitized and existing files are protected from overwrite. On headless Linux, do not store KEYRING_CRYPTFILE_PASSWORD in broadly readable config, shell profiles, logs, or service files; prefer a platform secret manager or tightly scoped secret injection.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ews-mail.py:299
Finding
Path Traversal and Arbitrary File Overwrite Through Attachment Filenames<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ews-mail.py`, lines 299-302 **Vulnerability Type**: Unsanitized file path construction **Risk Level**: High ### Vulnerable Code ```python if isinstance(att, FileAttachment) and att.content: path = os.path.join(dest_dir, att.name or f"attachment_{count}") with open(path, "wb") as f: f.write(att.content) ``` ### Technical Analysis The attachment name supplied by the Exchange server is used directly as a filesystem path component. The code does not reject absolute paths, parent-directory components such as `../`, or symbolic-link destinations. `os.path.join()` does not guarantee that the resulting path remains under `dest_dir`. If `att.name` is absolute, it can replace the preceding destination directory entirely. A relative name containing traversal components can similarly escape the intended directory. The file is opened in `wb` mode, which truncates an existing file before writing. Consequently, a malicious attachment filename can cause attacker-controlled attachment content to overwrite any file writable by the user running the Skill. ### Attack Path 1. An attacker sends a message containing a file attachment with a crafted filename, such as `../../.bashrc` or another traversal path accepted by the Exchange attachment interface. 2. The message appears in the target mailbox. 3. The user or Agent invokes `attachment-download` for that message. 4. The script concatenates the attacker-controlled filename with the selected destination directory without validation. 5. Path traversal causes the resolved location to escape the intended download directory. 6. The script opens the resolved path using `wb` and overwrites the target with attacker-controlled attachment content. 7. If the overwritten file is subsequently interpreted or executed, the attacker may obtain code execution with the privileges of the user running the Skill. ### Impact Assessment The direct impact is arbitrary ...[truncated 580 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat every attachment filename as untrusted input. 2. Remove directory components with `os.path.basename()` and reject empty, absolute, traversal, or special filenames. 3. Resolve both the destination directory and final path with `pathlib.Path.resolve()`, then verify that the final path remains beneath the destination directory. 4. Avoid silently overwriting existing files. Use exclusive creation mode such as `xb`, generate a unique filename, or require explicit user confirmation. 5. Consider symbolic-link attacks by opening files with platform-appropriate no-follow protections where available. 6. Apply restrictive file permissions when creating downloaded files. Example hardening pattern: ```python from pathlib import Path base = Path(dest_dir).expanduser().resolve() base.mkdir(parents=True, exist_ok=True) raw_name = att.name or f"attachment_{count}" safe_name = Path(raw_name).name if not safe_name or safe_name in {".", ".."}: raise ValueError("Invalid attachment filename") target = (base / safe_name).resolve() if target.parent != base: raise ValueError("Attachment path escapes destination directory") with target.open("xb") as f: f.write(att.content) ``` ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:9
Finding
Security-Sensitive Dependencies Are Not Version or Hash Pinned<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, line 9 **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Code ```yaml metadata: openclaw: emoji: "📧" requires: bins: ["python3"] pips: ["keyring", "exchangelib"] primaryEnv: "EWS_EMAIL" ``` The setup documentation also instructs users to install these packages, and `keyrings.alt`, without version constraints: ```bash pip3 install keyring exchangelib pip3 install keyring exchangelib keyrings.alt ``` ### Technical Analysis The Skill relies on `keyring`, `exchangelib`, and, in some environments, `keyrings.alt` for credential storage and access to Exchange. No exact versions or package hashes are specified. Package installation therefore resolves whichever compatible release is available from the configured Python package index at installation time. A newly compromised, malicious, or unexpectedly incompatible release could be installed without having been reviewed as part of this Skill audit. These dependencies operate in a security-sensitive context. They are imported into the same Python process that accesses the EWS password, master keyring password, mailbox messages, attachments, and outbound email functions. ### Attack Path 1. A dependency publisher account, package distribution channel, configured package index, or future package release is compromised. 2. An attacker publishes a malicious release under one of the required package names. 3. A user follows the installation instructions or the Skill environment automatically installs the unpinned dependency. 4. The package executes code during installation or when imported by `ews-mail.py`. 5. Malicious dependency code accesses process environment variables, keyring operations, EWS credentials, mailbox content, or the authenticated Exchange session. 6. The attacker may exfiltrate sensitive information or perform mailbox operations with the user's account permission ...[truncated 776 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to a reviewed exact version. 2. Maintain a lock file containing cryptographic hashes for all direct and transitive packages. 3. Install packages with hash enforcement, such as `pip install --require-hashes -r requirements.txt`. 4. Configure installation to use a trusted, explicitly selected package index. 5. Review dependency updates before changing pinned versions. 6. Run automated vulnerability and provenance checks against the locked dependency set. 7. Keep optional platform dependencies, including `keyrings.alt`, pinned and hashed in the same way. 8. Consider installing the Skill in an isolated virtual environment with only the filesystem and network access required for EWS operation. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (40)

Credential Access

High
Category
Privilege Escalation
Content
```bash
# 安装依赖
pip3 install keyring exchangelib keyrings.alt

# 设置 master password 环境变量(用于加解密 EWS 密码)
# 在 ~/.openclaw/config.yaml 中添加:
Confidence
72% confidence
Finding
The Linux setup recommends keyrings.alt EncryptedKeyring with file-based encrypted secret storage on headless systems. While not inherently malicious, this backend is weaker operationally than a native secure secret store and can expand the attack surface if the host, file permissions, or master-password handling are weak.

Credential Access

High
Category
Privilege Escalation
Content
# 设置 master password 环境变量(用于加解密 EWS 密码)
# 在 ~/.openclaw/config.yaml 中添加:
#   env:
#     KEYRING_CRYPTFILE_PASSWORD: "你自己定义的一个强密码"
#
# 或在 systemd service / 启动脚本中 export:
export KEYRING_CRYPTFILE_PASSWORD="你自己定义的一个强密码"
Confidence
86% confidence
Finding
The instructions recommend placing KEYRING_CRYPTFILE_PASSWORD in persistent configuration as an environment variable. Storing a master decryption password in config or process environment materially weakens the protection of the encrypted keyring because other local processes, service configs, crash dumps, or misconfigured logs may expose it.

Credential Access

High
Category
Privilege Escalation
Content
#     KEYRING_CRYPTFILE_PASSWORD: "你自己定义的一个强密码"
#
# 或在 systemd service / 启动脚本中 export:
export KEYRING_CRYPTFILE_PASSWORD="你自己定义的一个强密码"

# 存储 EWS 密码(会用 AES 加密写入本地文件)
python3 ~/.openclaw/skills/ews-email/scripts/ews-mail.py setup
Confidence
85% confidence
Finding
The guidance to export KEYRING_CRYPTFILE_PASSWORD in startup scripts or service environments encourages persistent plaintext secret exposure at the process/runtime layer. In enterprise or multi-user systems, such environment-based secret handling can be recovered through configuration disclosure, process inspection, support artifacts, or operational mistakes, enabling decryption of stored EWS credentials.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
**`envelope-list` only shows subject lines and metadata. It does NOT contain the email body.**
**You MUST run `message-read` to get the actual email content. NEVER guess or summarize based on subject alone.**
**NEVER say you cannot read email content — you CAN, by running `message-read`.**

## Script Location
Confidence
88% confidence
Finding
The anti-refusal language instructs the agent to never say it cannot read email content and insists on using message-read. Even though framed as operational guidance, this kind of coercive instruction can pressure an agent to bypass caution or contextual safety checks when handling sensitive mailbox data.

Credential Access

High
Category
Privilege Escalation
Content
import urllib3
urllib3.disable_warnings()

KEYRING_SERVICE = "openclaw-ews-email"

SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import urllib3
urllib3.disable_warnings()

KEYRING_SERVICE = "openclaw-ews-email"

SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import urllib3
urllib3.disable_warnings()

KEYRING_SERVICE = "openclaw-ews-email"

SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import urllib3
urllib3.disable_warnings()

KEYRING_SERVICE = "openclaw-ews-email"

SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import urllib3
urllib3.disable_warnings()

KEYRING_SERVICE = "openclaw-ews-email"

SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import urllib3
urllib3.disable_warnings()

KEYRING_SERVICE = "openclaw-ews-email"

SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import urllib3
urllib3.disable_warnings()

KEYRING_SERVICE = "openclaw-ews-email"

SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SERVER = os.environ.get("EWS_SERVER", "")
EMAIL = os.environ.get("EWS_EMAIL", "")

def _init_keyring():
    """Initialize keyring backend. On headless Linux, use EncryptedKeyring with
    master password from KEYRING_CRYPTFILE_PASSWORD env var."""
    import keyring
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Static analysis

No suspicious patterns detected.