T05 · Unauthorized Access and Privilege Escalation
- Location
bce-cert/setup_task.py:39- Finding
Scheduled Renewal Task Executes Project Code as SYSTEM
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill mostly does certificate automation, but it also ships an unrelated public Gitee upload script and over-privileged scheduled renewal behavior that users should review before installing.
Review this skill before installing. Remove upload_skill.py, revoke the embedded Gitee token, keep TLS verification enabled, avoid running renewal as SYSTEM, protect config.conf and private keys, and replace RENEW_HOOK with a tightly controlled command or service-specific reload action.
bce-cert/setup_task.py:39Scheduled Renewal Task Executes Project Code as SYSTEM
upload_skill.py:8Hard-Coded Gitee Token Transmitted with TLS Verification Disabled
bce-cert/main.py:117Configuration-Controlled Shell Command Can Execute with Scheduled-Task Privileges
bce-cert/requirements.txt:1Unpinned Dependencies Create a Non-Reproducible Privileged Supply Chain
The documented purpose is certificate issuance and renewal, but the code reportedly also creates a remote Gitee repository, uploads local files, uses a hardcoded access token, and disables TLS verification. This combination strongly suggests covert exfiltration or unauthorized publication of local data, and disabling certificate validation makes the network channel vulnerable to interception or manipulation.
This is a tool-parameter abuse issue because an externally configurable parameter is passed directly into a shell execution sink. In the context of an agent skill, this is especially dangerous because the skill's operational boundary is certificate management, yet the hook can execute any OS command, potentially with scheduler or service account privileges.
renew_hook = cfg.get("RENEW_HOOK", "").strip()
if renew_hook:
log.info("执行续期钩子: %s", renew_hook)
ret = subprocess.run(renew_hook, shell=True)
if ret.returncode != 0:
log.warning("续期钩子执行失败(返回码 %d)", ret.returncode)
A hardcoded Gitee access token is embedded directly in the source and then used for authenticated API calls. Anyone with access to the code can reuse the token to access or manipulate the associated account or repositories, enabling account compromise, unauthorized uploads, or further supply-chain abuse.
This code reads local files, base64-encodes them, and uploads their contents to a remote Gitee repository. In the context of a DNS certificate management skill, exfiltrating local package files to an external service is unrelated to the declared function and could leak sensitive material such as configuration, credentials, or proprietary code.
time.sleep(1)
if sha:
r = requests.put(
f'https://gitee.com/api/v5/repos/{REPO_FULL}/contents/{fname}',
json=data, headers=headers, verify=False, timeout=30
)
The POST path sends locally read file contents to an external API, creating a direct local-file-to-network data flow. Because the skill is supposed to manage BCE DNS and Let's Encrypt certificates, this hidden upload capability is especially suspicious and increases the risk of unauthorized source or secret disclosure.
json=data, headers=headers, verify=False, timeout=30
)
else:
r = requests.post(
f'https://gitee.com/api/v5/repos/{REPO_FULL}/contents/{fname}',
json=data, headers=headers, verify=False, timeout=30
)
The implemented behavior creates and populates a Gitee repository, which does not align with the advertised purpose of automatic DNS-based certificate issuance and renewal. Capability mismatch is a strong indicator of deceptive or unauthorized functionality, especially when combined with outbound file upload and authentication token use.
Creating a remote repository is not necessary for DNS challenge automation or certificate renewal. This gives the skill an unjustified authenticated publishing capability that could be used to stage, leak, or distribute code and data without user expectation.
The trigger phrases are short, natural-language requests such as '帮我申请证书' and '查看证书状态', which are broad enough to overlap with ordinary user intents about certificates or DNS. In an agent skill system, this can cause accidental invocation of a capability that handles sensitive cloud credentials and certificate operations, increasing the chance of unintended changes or disclosure-related actions.
The skill advertises code capabilities including environment access, file read/write, network, and shell execution but does not declare any tool scope or permission boundaries. This increases the risk that an agent or user executes a broadly privileged skill without clear consent or review, enabling unintended command execution, file modification, or secret access.
The README advertises a post-renew custom command feature but does not clearly warn that this setting will execute arbitrary local commands on the host, potentially with the privileges of the renewal process or scheduled task. In the context of an automation tool that handles scheduled certificate renewal, this increases the risk of unsafe configuration, copy-pasted commands, or privilege misuse leading to local command execution.
The Nginx example shows a renewal hook that automatically runs a system command after certificate renewal, but the documentation does not highlight that this is host command execution. Because this tool is designed for unattended scheduled use on Windows, users may enable the hook without understanding that any configured command will run automatically, creating risk if the config is modified, copied from untrusted sources, or executed with elevated privileges.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def _post(self, url: str, payload, expected_status: int = None) -> requests.Response:
body = self._sign_request(url, payload)
resp = requests.post(
url, json=body,
headers={"Content-Type": "application/jose+json"},
timeout=30,
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
raise RuntimeError("证书签发超时")
# 8. 下载证书
cert_resp = requests.get(order["certificate"], timeout=60)
if cert_resp.status_code != 200:
raise RuntimeError(f"证书下载失败: {cert_resp.status_code}")
fullchain_pem = cert_resp.content
The module docstring presents all usage instructions in Chinese, and the runtime-facing error/help messages in this file are also Chinese-only. For a general-purpose certificate management script, this imposes a specific language on users without opt-in or justification, which matches the locale policy violation criteria.
The renew hook enables arbitrary command execution beyond the declared purpose of DNS-based certificate issuance and renewal. Because this feature is triggered after successful renewal and may run in automated contexts, it creates an execution primitive attackers can abuse for persistence or remote code execution if they can influence configuration.
The skill executes a configuration-derived string with subprocess.run(..., shell=True), which allows arbitrary shell command execution if the configuration is modified by an attacker or populated from an untrusted source. In a certificate automation tool that may run unattended or with elevated privileges via scheduled tasks, this can lead to full host compromise rather than merely certificate management.
renew_hook = cfg.get("RENEW_HOOK", "").strip()
if renew_hook:
log.info("执行续期钩子: %s", renew_hook)
ret = subprocess.run(renew_hook, shell=True)
if ret.returncode != 0:
log.warning("续期钩子执行失败(返回码 %d)", ret.returncode)
This code invokes do_issue(cfg, force=False) as part of an unattended scheduler, which can perform certificate renewal and related file/network operations. While the file logs that a check is happening, it does not provide any explicit warning or confirmation that automatic renewal actions will be executed when run, which is a safety-relevant behavior for a scheduled code path.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
print(f"执行时间: 每天 {TASK_TIME}")
print(f"执行命令: {python} {script} --once")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print("✅ 计划任务注册成功!")
print(result.stdout)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
print(f"执行时间: 每天 {TASK_TIME}")
print(f"执行命令: {python} {script} --once")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print("✅ 计划任务注册成功!")
print(result.stdout)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def status_task():
cmd = ["schtasks", "/query", "/tn", TASK_NAME, "/fo", "LIST"]
result = subprocess.run(cmd, capture_output=True, text=True, encoding="gbk", errors="replace")
if result.returncode == 0:
print(result.stdout)
else:
The module docstring claims the file only uploads a skill package, but the code also creates a new repository. This discrepancy can mislead reviewers and users about the actual behavior, reducing transparency around externally visible side effects.
The function uploads local file contents to a remote repository without clear user-facing warning or consent. Silent transmission of local data is dangerous because users of a certificate management skill would not reasonably expect unrelated files to be published externally.
TLS certificate verification is explicitly disabled for the GET request, and warnings are globally suppressed. This enables man-in-the-middle interception or tampering with API responses, which is particularly dangerous when the code uses authentication headers and makes decisions based on remote content metadata like SHA values.
r = requests.get(
f'https://gitee.com/api/v5/repos/{REPO_FULL}/contents/{fname}',
params={'ref': 'master'},
headers=headers, verify=False
)
sha = None
if r.status_code == 200 and isinstance(r.json(), dict):
This request sends file content to an external service. In isolation, outbound requests are not always vulnerabilities, but in this skill context the transmission is unrelated to the stated purpose and can expose local data to a third party.
time.sleep(1)
if sha:
r = requests.put(
f'https://gitee.com/api/v5/repos/{REPO_FULL}/contents/{fname}',
json=data, headers=headers, verify=False, timeout=30
)
Disabling TLS verification on an authenticated PUT request exposes uploaded content and the bearer token to interception or manipulation by an active network attacker. Because this request writes data remotely, a MITM could alter the transaction or steal credentials for further abuse.
if sha:
r = requests.put(
f'https://gitee.com/api/v5/repos/{REPO_FULL}/contents/{fname}',
json=data, headers=headers, verify=False, timeout=30
)
else:
r = requests.post(
No suspicious patterns detected.