T01 · Skill Instruction Hijacking
- Location
skills/linkfox-aba-intelligent-query/SKILL.md:161- Finding
Silent Transmission of Conversation-Derived Feedback to a Third Party
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill supports the advertised product-research workflow, but it also silently reports user feedback and handles API credentials in ways users should review carefully.
Install only if you are comfortable with LinkFox receiving API requests and possible feedback summaries from your interactions. Avoid using the phone-code onboarding path in shared terminals, do not store the API key in shell startup files, prefer a scoped secret store, keep LINKFOX_* endpoint variables pinned to trusted HTTPS LinkFox hosts, and review any file before uploading because uploaded files become publicly accessible URLs.
skills/linkfox-aba-intelligent-query/SKILL.md:161Silent Transmission of Conversation-Derived Feedback to a Third Party
SKILL.md:122Mandatory LinkFox-Specific Modification of Every Agent Response
skills/linkfox-aba-intelligent-query/scripts/onboarding.py:454Generated API Key Disclosed in Plaintext Through Standard Output
skills/linkfox-aba-intelligent-query/scripts/onboarding.py:574Phone Number and SMS Verification Code Exposed Through Process Arguments
```
```python
p = sub.add_parser("send-code")
p.add_argument("phone")
p.set_defaults(func=_cmd_send_code)
p = sub.add_parser("login")
p.add_argument("phone")
p.add_argument("code")
p.add_argument("--channel", default="skill")
p.set_defaults(func=_cmd_login)
```
Equivalent command-line interfaces are present in the duplicated onboarding scripts used by the other API Skills.
### Technical Analysis
The phone number and active SMS verification code are accepted as positional command-line arguments. Depending on the execution environment, command arguments may be exposed through:
- Shell history
- Process inspection tools
- Agent command transcripts
- CI/CD logs
- Process supervisors
- Diagnostic and crash reports
Although SMS codes are short-lived, they are active authentication secrets during their validity period. Phone numbers are also personal data.
### Attack Path
1. The user or agent runs the documented `login` command.
2. The phone number and verification code are placed in the process argument vector.
3. A local process observer, log collector, terminal-history reader, or orchestration system records the arguments.
4. The observer captures the code before expiration.
5. The code and phone number are used to attempt unauthorized login or account enrollment.
### Impact Assessment
A successful exploit may permit temporary account authentication, API-key generation, or unauthorized account access, subject to server-side verification and code lifetime. Even when the code has expired, the process history may retain the user's p
...[truncated 16 chars]skills/linkfox-aba-intelligent-query/references/onboarding.md:11Long-Lived API Key Persisted in Plaintext Shell Startup Files
skills/linkfox-aba-intelligent-query/scripts/onboarding.py:64Credential-Bearing Requests Can Be Redirected Through Unvalidated Environment Overrides
skills/linkfox-file-upload/SKILL.md:32Unpinned Third-Party Dependency Installation Guidance
download_media() performs outbound requests to an attacker-controlled URL with only a superficial scheme check (http/https). This is a classic SSRF primitive: if untrusted input reaches this function, the agent can be induced to contact internal services, cloud metadata endpoints, or other sensitive network locations and save the response locally.
req = Request(url, headers={"User-Agent": "LinkFox-Skill/2.0"})
try:
with urlopen(req, timeout=timeout) as resp:
# 从 Content-Type 进一步修正扩展名
if guessed_ext == "bin":
ct = resp.headers.get("Content-Type", "")
The POST target URL is derived from environment-controlled base URLs and the request may include sensitive authentication material such as access tokens, API keys, phone numbers, SMS codes, and generated API tokens. If an attacker can influence the environment, they can redirect these requests to attacker-controlled infrastructure and exfiltrate credentials or onboarding data; this is especially risky because the script performs login and token-generation flows.
except RuntimeError as e:
return {"_error": str(e)}
try:
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
return r.json()
except Exception as e:
body_text = ""
The gateway request URL is built from environment-controlled base configuration and is used with an Authorization header carrying the LinkFox API key. An attacker who can set the environment can redirect billing, package, account, and order requests to a malicious endpoint and harvest the API key or manipulate downstream behavior.
headers["Content-Type"] = "application/json"
req = Request(url, method=method, data=body_bytes, headers=headers)
try:
with urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
status = e.code
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
except RuntimeError as e:
return {"_error": str(e)}
try:
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
return r.json()
except Exception as e:
body_text = ""
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers["Content-Type"] = "application/json"
req = Request(url, method=method, data=body_bytes, headers=headers)
try:
with urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
status = e.code
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
req = _lf_Request(url, headers={"User-Agent": "LinkFox-Skill/2.0"})
try:
with _lf_urlopen(req, timeout=timeout) as resp:
if guessed_ext == "bin":
ct = resp.headers.get("Content-Type", "")
if "mp4" in ct:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
}
req = Request(url, data=data, headers=headers, method="POST")
try:
with urlopen(req, timeout=HTTP_TIMEOUT) as response:
return json.loads(response.read().decode("utf-8"))
except HTTPError as e:
body = e.read().decode("utf-8") if e.fp else ""
The script builds request destinations from environment variables such as LINKFOX_LOGIN_API_URL and LINKFOX_AGENT_USER_API_URL, then sends sensitive data including phone numbers, SMS codes, access tokens, refresh tokens, and generated API keys to those endpoints. In an untrusted skill environment, attacker-controlled environment variables can silently redirect these secrets to an arbitrary server, turning onboarding into credential exfiltration.
except RuntimeError as e:
return {"_error": str(e)}
try:
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
return r.json()
except Exception as e:
body_text = ""
The gateway URL is also derived from environment variables and then used by urllib with the Authorization header populated from LINKFOX_AGENT_API_KEY. If an attacker can influence LINKFOX_AGENT_API_URL or LINKFOX_TOOL_GATEWAY, the script will send the user's API key and subsequent account/order traffic to an attacker-controlled endpoint.
headers["Content-Type"] = "application/json"
req = Request(url, method=method, data=body_bytes, headers=headers)
try:
with urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
status = e.code
The request sent via urlopen includes multiple HTTP headers populated directly from environment variables, and the destination base URL is also environment-controlled through LINKFOX_TOOL_GATEWAY. In an agent/runtime setting where environment variables may be attacker-influenced, this enables SSRF-like outbound requests, metadata/header exfiltration, and misuse of the API key against arbitrary hosts.
method="POST",
)
try:
with urlopen(req, timeout=150) as response:
return json.loads(response.read().decode("utf-8"))
except HTTPError as e:
body = e.read().decode("utf-8") if e.fp else ""
The requests-based POST helper ultimately sends sensitive inputs, including phone numbers, SMS codes, access tokens, refresh tokens, and generated API keys, to URLs whose base is partially controlled by environment variables. In a skill execution environment, those variables may be influenced by an attacker or a compromised wrapper, enabling credential exfiltration to an attacker-controlled endpoint with little visibility to the user.
except RuntimeError as e:
return {"_error": str(e)}
try:
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
return r.json()
except Exception as e:
body_text = ""
The urllib gateway helper builds outbound requests using a base URL derived from environment variables and attaches the LinkFox API key in the Authorization header. If an attacker can influence the environment, they can redirect requests to an arbitrary server and capture the API key and account/order metadata, turning this into a straightforward secret exfiltration path.
headers["Content-Type"] = "application/json"
req = Request(url, method=method, data=body_bytes, headers=headers)
try:
with urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
status = e.code
The request sent via urlopen includes multiple headers derived directly from environment variables, including SESSION_ID, MESSAGE_ID, MODE_ID, APP_NAME, and especially an environment-controlled base URL through LINKFOX_TOOL_GATEWAY. If an attacker can influence the runtime environment, they can redirect requests to an arbitrary host and cause sensitive metadata and the API key to be exfiltrated in outbound requests. In an agent/skill environment, environment variables are often part of orchestration and can be less trusted than hardcoded values, which makes this more dangerous than a normal local script.
method="POST",
)
try:
with urlopen(req, timeout=150) as response:
return json.loads(response.read().decode("utf-8"))
except HTTPError as e:
body = e.read().decode("utf-8") if e.fp else ""
The POST destination is derived from environment-controlled base URLs via _login_base() and _agent_user_base(), then used in requests.post while carrying sensitive data such as phone numbers, SMS codes, access tokens, refresh tokens, and generated API keys in headers/bodies. If an attacker can influence environment variables, they can redirect these authentication flows to attacker-controlled infrastructure and exfiltrate credentials and session material.
except RuntimeError as e:
return {"_error": str(e)}
try:
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
return r.json()
except Exception as e:
body_text = ""
The gateway URL is built from environment-controlled values and passed to urllib.request.urlopen with the Authorization header populated from LINKFOX_AGENT_API_KEY. This creates an exfiltration path where a manipulated environment can cause authenticated requests, user/order data, and billing actions to be sent to an attacker-controlled endpoint.
headers["Content-Type"] = "application/json"
req = Request(url, method=method, data=body_bytes, headers=headers)
try:
with urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
status = e.code
get_sts_voucher() builds the destination from LINKFOX_TOOL_GATEWAY and sends the API token in the Authorization header to that endpoint. If an attacker can influence environment variables, they can redirect the request to an untrusted host and capture the API credential, turning this into credential exfiltration via SSRF-like endpoint injection.
headers={"Content-Type": "application/json", "Authorization": api_token},
)
try:
with urlopen(req, timeout=30) as resp:
body = json.loads(resp.read().decode())
break
except urllib.error.HTTPError as e:
download_media() performs outbound requests to an attacker-controlled url with only a scheme check for http/https. This enables SSRF against internal services or cloud metadata endpoints, and can also be abused to make the agent download large or sensitive content into local storage without any host allowlist, DNS/IP validation, or redirect restrictions.
req = Request(url, headers={"User-Agent": "LinkFox-Skill/2.0"})
try:
with urlopen(req, timeout=timeout) as resp:
# 从 Content-Type 进一步修正扩展名
if guessed_ext == "bin":
ct = resp.headers.get("Content-Type", "")
The request sent via urlopen includes attacker- or environment-controlled values such as LINKFOX_TOOL_GATEWAY and multiple metadata headers from environment variables. This can enable data exfiltration to an unintended endpoint or SSRF-like behavior if the gateway base URL is overridden, especially because request parameters and API credentials are also transmitted.
method="POST",
)
try:
with urlopen(req, timeout=150) as response:
return json.loads(response.read().decode("utf-8"))
except HTTPError as e:
body = e.read().decode("utf-8") if e.fp else ""
The POST target URL is derived from environment-controlled base URLs, and the request can carry highly sensitive data including phone numbers, SMS codes, access tokens, refresh tokens, generated API keys, and browser-like identity headers. If an attacker can influence environment variables in the skill runtime, they can redirect these requests to attacker infrastructure and exfiltrate credentials or session material.
except RuntimeError as e:
return {"_error": str(e)}
try:
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
return r.json()
except Exception as e:
body_text = ""
The gateway request URL is also constructed from environment-controlled base configuration and is used with an Authorization header containing the API key. An attacker who can set the environment can redirect account, package, and order operations to a malicious endpoint and capture the API key and related account metadata.
headers["Content-Type"] = "application/json"
req = Request(url, method=method, data=body_bytes, headers=headers)
try:
with urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
status = e.code
The request sent via urlopen includes multiple headers populated directly from environment variables, and the destination base URL is also environment-controlled via LINKFOX_TOOL_GATEWAY. In an agent/runtime setting, environment variables are often influenced by surrounding orchestration, so this creates a server-side request/data exfiltration risk: secrets such as the API key and session metadata can be sent to an attacker-controlled endpoint if the gateway variable is overridden.
method="POST",
)
try:
with urlopen(req, timeout=150) as response:
return json.loads(response.read().decode("utf-8"))
except HTTPError as e:
body = e.read().decode("utf-8") if e.fp else ""
The URL used by requests.post is derived from environment-configurable base URLs, and these requests carry sensitive data including phone numbers, SMS codes, access tokens, refresh tokens, and generated API tokens. If an attacker can influence environment variables in the skill runtime, they can redirect authentication traffic to attacker-controlled infrastructure and exfiltrate credentials.
except RuntimeError as e:
return {"_error": str(e)}
try:
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
return r.json()
except Exception as e:
body_text = ""
The gateway URL passed into urllib.request.urlopen is also built from environment-controlled base configuration and is used with the Authorization header containing the API key. A compromised or user-influenced environment can redirect these authenticated requests to an attacker-controlled server, leaking the API key and enabling unauthorized account or billing actions.
headers["Content-Type"] = "application/json"
req = Request(url, method=method, data=body_bytes, headers=headers)
try:
with urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
status = e.code
The code accepts an arbitrary URL and fetches it with urlopen, then writes the response into local session storage. Although it restricts schemes to http/https, it still enables server-side request forgery against internal or metadata endpoints and can be abused to pull untrusted content into the workspace. In this skill context, remote media downloading is not clearly required for cross-cultural product scouting, which increases concern.
req = Request(url, headers={"User-Agent": "LinkFox-Skill/2.0"})
try:
with urlopen(req, timeout=timeout) as resp:
# 从 Content-Type 进一步修正扩展名
if guessed_ext == "bin":
ct = resp.headers.get("Content-Type", "")
The request headers include multiple environment-derived values, including the API key and session/app identifiers, and the destination host is controllable through LINKFOX_TOOL_GATEWAY. That creates an SSRF/exfiltration path where sensitive credentials and metadata can be sent to an attacker-controlled endpoint if the environment is influenced by an untrusted party.
method="POST",
)
try:
with urlopen(req, timeout=150) as response:
return json.loads(response.read().decode("utf-8"))
except HTTPError as e:
body = e.read().decode("utf-8") if e.fp else ""