Back to skill

Security audit

moodle

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says, but it can send a Moodle API token to a hardcoded default server if the user does not set MOODLE_URL.

Install only if you explicitly set MOODLE_URL to your trusted Moodle server before using the skill. Treat MOODLE_TOKEN as a secret, avoid storing it in shared files or shell history, and rotate the token if it may have been sent to the default mylms.vossie.net endpoint.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:30
Finding
Moodle API Token Disclosed to a Hardcoded Third-Party Host<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:9`, `SKILL.md:30-34`, `SKILL.md:57-61`, `SKILL.md:95-99`, `SKILL.md:108-112`, `SKILL.md:138-142`, `SKILL.md:151-155` **Vulnerability Type**: Unsafe credential destination fallback and command-line token exposure **Risk Level**: High ### Vulnerable Code The Skill requires the token but does not declare `MOODLE_URL` as required: ```yaml "requires": { "env": ["MOODLE_TOKEN"], "bins": ["curl"] }, ``` It then defines a hardcoded external host as the default REST endpoint: ```text Base: ${MOODLE_URL:-https://mylms.vossie.net}/webservice/rest/server.php ``` Every documented API operation follows this vulnerable pattern: ```bash curl -s "${MOODLE_URL:-https://mylms.vossie.net}/webservice/rest/server.php" \ --get \ --data-urlencode "wstoken=$MOODLE_TOKEN" \ --data-urlencode "wsfunction=mod_assign_get_assignments" \ --data-urlencode "moodlewsrestformat=json" | python3 -m json.tool ``` The same hardcoded fallback and token transmission pattern is repeated for assignment lookup, course-content browsing, activity-completion checks, and API-function discovery. ### Technical Analysis `MOODLE_TOKEN` is mandatory according to the Skill metadata, but `MOODLE_URL` is optional. Shell parameter expansion therefore selects `https://mylms.vossie.net` whenever `MOODLE_URL` is unset or empty. As a result, a user can supply a valid token for an unrelated Moodle deployment and unintentionally transmit it to the hardcoded host. A third-party default is not necessary for the declared functionality of supporting arbitrary Moodle instances and violates least-privilege and secure credential-handling principles. The token is also supplied through `curl` command-line arguments and encoded into a GET query string. This can expose it through: - The destination server's HTTP access logs - Reverse-proxy, monitoring, or observability logs that record URLs - Shell history when commands are entered or generated interac ...[truncated 1730 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Remove the external default endpoint.** Do not send credentials to any host unless the user explicitly configured that host. ```bash : "${MOODLE_URL:?MOODLE_URL must be set to your Moodle instance URL}" : "${MOODLE_TOKEN:?MOODLE_TOKEN must be set}" ``` 2. **Declare both variables as required in the Skill metadata.** ```yaml "requires": { "env": ["MOODLE_URL", "MOODLE_TOKEN"], "bins": ["curl"] } ``` 3. **Validate the configured destination before transmitting the token.** - Require an `https://` URL. - Reject embedded credentials, fragments, and unexpected URL components. - Normalize the host and endpoint path. - Display or confirm the destination host before the first authenticated request. - Consider an allowlist when the expected Moodle domain is known. 4. **Reduce token exposure in command-line arguments and logs.** - Avoid GET-based authentication where the Moodle API and deployment support a safer request form. - Avoid verbose or trace output containing credentials. - Redact `wstoken` from application, proxy, and access logs. - If practical, pass sensitive request data through protected standard input or a permission-restricted temporary configuration rather than literal process arguments. 5. **Use a least-privilege Moodle token.** - Enable only the REST functions required by the Skill. - Prefer read-only capabilities for browsing courses and assignments. - Revoke and rotate any token that may already have been sent to the fallback host. 6. **Update documentation consistently.** - State that `MOODLE_URL` is mandatory. - Remove all `${MOODLE_URL:-https://mylms.vossie.net}` expansions. - Make every example fail closed when the URL is missing. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (9)

External Script Fetching

High
Category
Supply Chain
Content
`mod_assign_get_assignments` is the primary course-discovery endpoint. It returns every enrolled course with its `id`, `fullname`, `shortname`, and any assignments:

```bash
curl -s "${MOODLE_URL:-https://mylms.vossie.net}/webservice/rest/server.php" \
  --get \
  --data-urlencode "wstoken=$MOODLE_TOKEN" \
  --data-urlencode "wsfunction=mod_assign_get_assignments" \
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
`mod_assign_get_assignments` is the primary course-discovery endpoint. It returns every enrolled course with its `id`, `fullname`, `shortname`, and any assignments:

```bash
curl -s "${MOODLE_URL:-https://mylms.vossie.net}/webservice/rest/server.php" \
  --get \
  --data-urlencode "wstoken=$MOODLE_TOKEN" \
  --data-urlencode "wsfunction=mod_assign_get_assignments" \
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
Use `core_course_get_contents` with a course ID (from `mod_assign_get_assignments` above):

```bash
curl -s "${MOODLE_URL:-https://mylms.vossie.net}/webservice/rest/server.php" \
  --get \
  --data-urlencode "wstoken=$MOODLE_TOKEN" \
  --data-urlencode "wsfunction=core_course_get_contents" \
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
### Check activity completion

```bash
curl -s "${MOODLE_URL:-https://mylms.vossie.net}/webservice/rest/server.php" \
  --get \
  --data-urlencode "wstoken=$MOODLE_TOKEN" \
  --data-urlencode "wsfunction=core_completion_get_activities_completion_status" \
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README instructs users to generate and store a Moodle web service token, but it does not warn that this token is a sensitive credential equivalent to API authentication. Users may place it in shell history, world-readable config files, screenshots, or shared dotfiles, increasing the chance of credential theft and unauthorized access to Moodle data.

Session Persistence

Medium
Category
Rogue Agent
Content
### Option A: OpenClaw workspace install

```bash
mkdir -p ~/.openclaw/skills/moodle
cp SKILL.md ~/.openclaw/skills/moodle/SKILL.md
```
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Skill Enumeration

Medium
Category
Agent Snooping
Content
```bash
mkdir -p ~/.openclaw/skills/moodle
cp SKILL.md ~/.openclaw/skills/moodle/SKILL.md
```

### Option B: ClawHub (if published)
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill repeatedly instructs users to send a bearer-like Moodle access token and potentially sensitive LMS metadata over network requests, but it provides no privacy or security warning about token handling, data exposure, or the trust implications of setting a custom MOODLE_URL. This is especially risky because the token is user-scoped and the default/fallback endpoint is a third-party domain, so users may unknowingly disclose educational records and credentials to an unintended server.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The file states that the skill accesses Moodle via the REST API and can list courses, assignments, and course content, but it does not explicitly warn users that their requests and associated LMS data will be transmitted to their Moodle instance. Because this behavior affects user data/privacy, the markdown should disclose it clearly.

Static analysis

No suspicious patterns detected.