Back to skill

Security audit

Xuexitong Homework Submit

Security checks for vulnerabilities and agentic risk

Overview

The skill’s homework automation purpose is clear, but its credential handling can expose Chaoxing session cookies through unvalidated URLs and plaintext HTTP uploads.

Review this skill before installing. It automates real homework account actions and uses your Chaoxing cookie. Only use it with your own account, avoid untrusted task or homework URLs, protect and rotate the cookie if you have used the handwriting upload path, and prefer a fixed version that validates Chaoxing HTTPS hosts, removes the global raw Cookie header, propagates the selected cookie to child processes, and pins dependencies.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/xuexitong_submit.py:69
Finding
Authentication Cookie Disclosure Through Unvalidated Request URLs<![CDATA[ ## Vulnerability Details **File Location**: - `scripts/xuexitong_submit.py:69-94` - `scripts/xuexitong_submit.py:129-181` - `scripts/xuexitong_submit.py:220-245` - `scripts/xuexitong_hw_pipeline.py:93-127` - `scripts/xuexitong_hw_pipeline.py:326-332` - `scripts/xuexitong_hw_pipeline.py:406-420` **Vulnerability Type**: Sensitive credential disclosure through unvalidated outbound requests **Risk Level**: High ### Vulnerable Code The session places the complete authentication cookie in a global request header: ```python def session(cookie_header: str, ua: str = DEFAULT_UA) -> requests.Session: """Create a session that sends cookies as a CookieJar (more reliable than raw Cookie header).""" s = requests.Session() s.headers.update( { "User-Agent": ua, "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9", "Referer": "https://mooc1-api.chaoxing.com/work/stu-work", # Many Chaoxing endpoints behave differently depending on whether cookies are sent # as a raw header vs cookie jar. We set BOTH for maximum compatibility. "Cookie": cookie_header, } ) # Populate cookie jar for kv in cookie_header.split(";"): kv = kv.strip() if not kv or "=" not in kv: continue k, v = kv.split("=", 1) k = k.strip() v = v.strip() if not k: continue s.cookies.set(k, v, domain=".chaoxing.com") return s ``` The same authenticated session is then used with caller-controlled URLs without validating their scheme, hostname, or port: ```python def resolve_mtask_to_dohomework(s: requests.Session, task_url: str, sleep_ms: int = 0) -> str: if sleep_ms: time.sleep(sleep_ms / 1000) r = s.get(task_url, timeout=30) r.raise_for_status() html = r.text # Find doHomeWork URL inside the page # pattern i ...[truncated 4273 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the global raw cookie header: ```python s.headers.pop("Cookie", None) ``` Use only a domain-scoped `RequestsCookieJar`. 2. Validate every URL before making a request: - Require the `https` scheme. - Require an explicit hostname allowlist, such as `mooc1-api.chaoxing.com`. - Reject embedded credentials, unexpected ports, malformed hostnames, and scheme-relative URLs. - Compare normalized hostnames rather than using substring or suffix-only checks. 3. Disable automatic redirects or validate each redirect destination: ```python response = s.get(url, timeout=30, allow_redirects=False) ``` Follow redirects only after applying the same scheme and hostname checks. 4. Centralize outbound URL validation so `resolve`, `fetch`, `save`, `submit`, the pipeline, and the scanner use the same policy. 5. Use separate sessions for separate services. Each session should contain only the minimum cookies required by that specific Chaoxing endpoint. 6. Add regression tests proving that attacker-controlled hosts, HTTP URLs, deceptive subdomains, user-info URLs, and off-domain redirects are rejected before any credential is transmitted. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/xuexitong_hw_pipeline.py:256
Finding
Authenticated Homework Upload Uses Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `scripts/xuexitong_hw_pipeline.py:256-269` **Vulnerability Type**: Plaintext transmission of authentication credentials and homework content **Risk Level**: High ### Vulnerable Code ```python def upload_notice_file(s: requests.Session, file_path: Path) -> dict: url = "http://notice.chaoxing.com/pc/files/uploadNoticeFile" with open(file_path, "rb") as f: files = {"attrFile": (f"{int(time.time()*1000)}{file_path.suffix}", f)} r = s.post(url, files=files, timeout=30) r.raise_for_status() j = r.json() if not j.get("status"): return {"ok": False, "resp": j} # stable url without query u = (j.get("url") or "").split("?")[0] return {"ok": True, "url": u, "raw": j} ``` The supplied session is created with the complete Chaoxing cookie as a raw default header: ```python def make_session(cookies: dict) -> requests.Session: s = requests.Session() cookie_header = "; ".join([f"{k}={v}" for k, v in cookies.items()]) s.headers.update( { "User-Agent": DEFAULT_UA, "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9", "Referer": "https://mooc1-api.chaoxing.com/work/stu-work", # Some endpoints behave differently if cookies are not also present as a raw header "Cookie": cookie_header, } ) # populate cookie jar (best-effort) for k, v in cookies.items(): s.cookies.set(k, v, domain=".chaoxing.com") return s ``` ### Technical Analysis The upload endpoint explicitly uses `http://`, so transport encryption and server authentication are absent. The request contains the rendered homework answer image and, because the session defines a raw default `Cookie` header, the user's authentication cookie. An on-path attacker can passively read the traffic or actively modify the request and response. The c ...[truncated 1343 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the endpoint with its verified HTTPS equivalent: ```python url = "https://notice.chaoxing.com/pc/files/uploadNoticeFile" ``` 2. Fail closed if HTTPS is unavailable. Do not downgrade to HTTP. 3. Use a dedicated upload session containing only the cookies required by `notice.chaoxing.com`; do not reuse a session carrying the complete Chaoxing cookie string. 4. Remove the global raw `Cookie` header and rely on narrowly scoped cookie-jar entries. 5. Disable or strictly validate redirects. Reject any redirect to HTTP or to a hostname outside the approved Chaoxing allowlist. 6. Validate the returned upload URL: - Require HTTPS. - Require an approved Chaoxing hostname. - Reject embedded credentials and unexpected ports. - Reject malformed or non-absolute URLs. 7. Treat existing credentials as potentially exposed if the plaintext upload has previously been used, and advise users to invalidate affected sessions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/xuexitong_scan_pending.py:42
Finding
Custom Credential Selection Is Not Propagated to Child Processes<![CDATA[ ## Vulnerability Details **File Location**: - `scripts/xuexitong_scan_pending.py:42-81` - `scripts/xuexitong_hw_pipeline.py:298-319` - `scripts/xuexitong_hw_pipeline.py:350-388` - `scripts/xuexitong_hw_pipeline.py:405-408` **Vulnerability Type**: Credential-context confusion and unintended access to the default account **Risk Level**: Medium ### Vulnerable Code The scanner accepts a custom credential path but does not pass it to the child commands: ```python def run_submit_py(args: list[str]) -> str: here = os.path.dirname(os.path.abspath(__file__)) py = os.path.join(here, "xuexitong_submit.py") vpy = os.path.join(os.path.dirname(here), ".venv", "bin", "python") cmd = [vpy, py] + args # Avoid repeated GitHub checks for each subprocess call in scan flow. env = dict(os.environ) env["XUEXITONG_SKIP_UPDATE_CHECK"] = "1" return subprocess.check_output(cmd, env=env).decode("utf-8") ``` ```python ap.add_argument( "--cookie", default=os.path.expanduser("~/.openclaw/credentials/xuexitong_cookie.txt"), help="cookie file path", ) ... tasks = json.loads(run_submit_py(["list"]))["tasks"] tasks = tasks[: args.limit] resolved = [] resolve_failures = [] seen = set() for t in tasks: try: do = run_submit_py(["resolve", "--task-url", t]).strip() except Exception: resolve_failures.append(t) continue ``` The handwriting pipeline has the same issue when invoking the save operation: ```python def temp_save_homework(*, dohomework_url: str, work_json_path: Path, answers_html_path: Path, out_result: Path): # call existing submit script to ensure per-question hidden fields are handled skill_dir = Path(__file__).resolve().parents[1] submit_py = skill_dir / "scripts" / "xuexitong_submit.py" venv_py = skill_dir / ".venv" / "bin" / "python" cmd = [ str(venv_py), str(submit_py), "save", "--dohomework-url", dohomework_url, "--answers" ...[truncated 2662 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pass the selected credential path to every child process. Because `--cookie` is a global argument in `xuexitong_submit.py`, place it before the subcommand: ```python run_submit_py(["--cookie", args.cookie, "list"]) ``` ```python run_submit_py([ "--cookie", args.cookie, "resolve", "--task-url", t, ]) ``` 2. Add a `cookie_path` parameter to `temp_save_homework()` and include it in the child command: ```python cmd = [ str(venv_py), str(submit_py), "--cookie", cookie_path, "save", ... ] ``` 3. Prefer direct function calls with an explicitly supplied authenticated session over subprocesses that reconstruct authentication state from implicit defaults. 4. Remove silent credential fallback for state-changing workflows. Require an explicit cookie path or clearly report the resolved credential source before performing network operations. 5. Add multi-account integration tests verifying that listing, resolving, uploading, and saving all use the same selected credential. 6. Before saving, verify that the account identity and homework identifiers returned by the server match those established during initialization. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Env Variable Harvesting

High
Category
Data Exfiltration
Content
cmd = [vpy, py] + args

    # Avoid repeated GitHub checks for each subprocess call in scan flow.
    env = dict(os.environ)
    env["XUEXITONG_SKIP_UPDATE_CHECK"] = "1"

    return subprocess.check_output(cmd, env=env).decode("utf-8")
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises and instructs use of scripts that require network, shell, file read/write, and access to credential-like cookie files, but the manifest does not declare any explicit tool scope or permissions boundaries. This is dangerous because operators and policy systems cannot accurately constrain what the skill is allowed to do, increasing the risk of over-privileged execution, unintended credential access, and silent network exfiltration if the referenced scripts are malicious or compromised.

Session Persistence

Medium
Category
Rogue Agent
Content
- generate editable answers template (with stems)
- (after user edits) render handwritten PNGs on grid background
- upload PNGs to Chaoxing cldisk (uploadNoticeFile)
- write HTML <img> answers.json
- temp-save (暂存) to homework

Design goals:
Confidence
82% confidence
Finding
The pipeline intentionally persists sensitive workflow artifacts to disk, including question content, answer text, uploaded image URLs, and HTML answer payloads. In the context of an authenticated homework automation tool, these files may expose private academic content or enable reuse/manipulation by other local users or malware if stored with weak filesystem protections.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"--bg",
        str(bg_path),
    ]
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        raise SystemExit(p.stderr or p.stdout)
    try:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"--bg",
        str(bg_path),
    ]
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        raise SystemExit(p.stderr or p.stdout)
    try:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The pipeline uploads homework answer images to http://notice.chaoxing.com/pc/files/uploadNoticeFile over plain HTTP, exposing authentication cookies, uploaded content, and server responses to interception or modification by a network attacker. In this skill's context, the images contain user homework answers and the session is authenticated, so MITM tampering could leak academic data or hijack the Chaoxing session.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
env = dict(os.environ)
    env["XUEXITONG_SKIP_UPDATE_CHECK"] = "1"

    return subprocess.check_output(cmd, env=env).decode("utf-8")


def main():
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The script reads a cookie from the default credentials path and uses it to authenticate requests, which is a sensitive credential access operation. While the module docstring explains scanning behavior, it does not disclose that authentication cookies will be read from disk and used for network requests.

Missing User Warnings

Medium
Confidence
82% confidence
Finding
The script sends authenticated HTTP requests to homework URLs using the user's cookie-backed session, which transmits user/session data over the network. The current description focuses on scanning assignments but does not explicitly warn that authenticated requests will be made to the remote service.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This code constructs a requests session that injects the user's Chaoxing cookies into all subsequent HTTP requests, which is a safety-relevant network operation involving credentials. Although the script uses these cookies as part of its function, there is no user-facing disclosure in comments, prompts, or CLI help warning that the provided cookie file will be sent to remote services for authentication.

Session Persistence

Medium
Category
Rogue Agent
Content
def session(cookie_header: str, ua: str = DEFAULT_UA) -> requests.Session:
    """Create a session that sends cookies as a CookieJar (more reliable than raw Cookie header)."""
    s = requests.Session()
    s.headers.update(
        {
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.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
Confidence
96% confidence
Finding
The dependency specification uses a lower-bound constraint only (`requests>=2.31.0`), which allows installation of any future major or minor release. This harms build reproducibility and can unintentionally pull in vulnerable or breaking versions, especially significant for an automation skill that logs into third-party services and handles homework submission workflows over the network.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
83% confidence
Finding
The manifest does not pin the `requests` version, so there is no way to verify whether deployment will install a release affected by known advisories. In this skill's context, which appears to interact with remote Chaoxing endpoints and likely handles authentication/session data, an affected `requests` release could increase the risk of credential leakage, TLS/request-handling flaws, or other client-side security issues.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The session headers hard-code `Accept-Language: zh-CN,zh;q=0.9`, which forces a specific language/locale preference for all requests. This is a natural-language policy concern because the file does not offer user opt-in or explain why the locale restriction is required.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The session headers hard-code Accept-Language to "zh-CN,zh;q=0.9", which forces a specific locale for all requests. The file does not offer a user opt-in or configuration option, and no justification is documented for requiring this locale.

Static analysis

No suspicious patterns detected.