T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/mail-api.py:488- Finding
Unauthenticated Local API Grants Full Mailbox Control
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This email skill is mostly coherent, but it exposes powerful mailbox actions through an unauthenticated local API and documents a root-running system service.
Install only if you are comfortable granting a local service broad control over the configured mailbox. Run it under a dedicated unprivileged account, add authentication or a protected socket before enabling the API, keep the webhook unset unless it points to a trusted endpoint, avoid saving attachments from untrusted mail until filenames are sanitized, and pin dependencies for reproducible installs.
scripts/mail-api.py:488Unauthenticated Local API Grants Full Mailbox Control
scripts/check_inbox.py:75Untrusted Attachment Filenames Permit Path Traversal and Arbitrary File Overwrite
references/systemd.md:19Documented System Service Runs the Mail API as Root by Default
SKILL.md:1Runtime Dependencies Are Installed Without Version or Integrity Pinning
The webhook URL is taken from environment configuration and used to POST full message data, including message bodies and metadata, to whatever endpoint is configured. In this skill context, that creates a direct exfiltration path for sensitive email contents and can also be abused as SSRF if an attacker can influence environment configuration or deployment settings.
IDLE_WEBHOOK, data=data,
headers={"Content-Type": "application/json"},
)
urllib.request.urlopen(req, timeout=10)
except Exception as e:
_idle_status["error"] = f"webhook: {e}"
The script sends contact and note data to a network endpoint whose base URL is taken directly from the IMAP_MAIL_API environment variable without validation or transport guarantees. If that variable is tampered with, the tool can exfiltrate sensitive CRM data to an attacker-controlled service, and the default/plain HTTP transport also weakens confidentiality and integrity.
headers={"Content-Type": "application/json"} if data else {},
)
try:
resp = urllib.request.urlopen(req, timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
body = e.read().decode()
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def api_get(path):
url = API.rstrip("/") + path
try:
resp = urllib.request.urlopen(urllib.request.Request(url), timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def api_get(path):
url = API.rstrip("/") + path
try:
resp = urllib.request.urlopen(urllib.request.Request(url), timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def api_get(path):
url = API.rstrip("/") + path
try:
resp = urllib.request.urlopen(urllib.request.Request(url), timeout=20)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def api_get(path):
url = API.rstrip("/") + path
try:
resp = urllib.request.urlopen(urllib.request.Request(url), timeout=20)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers={"Content-Type": "application/json"},
)
try:
resp = urllib.request.urlopen(req, timeout=30)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers={"Content-Type": "application/json"},
)
try:
resp = urllib.request.urlopen(req, timeout=30)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
The webhook URL is taken from environment configuration and the code posts full message data to it, including message bodies and metadata. If an attacker can influence configuration or if the webhook points to an unintended host, this becomes an exfiltration channel for sensitive email contents; the optional unverified TLS mode further increases the risk of interception or redirection.
IDLE_WEBHOOK, data=data,
headers={"Content-Type": "application/json"},
)
urllib.request.urlopen(req, timeout=10)
except Exception as e:
_idle_status["error"] = f"webhook: {e}"
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers={"Content-Type": "application/json"} if data else {},
)
try:
resp = urllib.request.urlopen(req, timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
body = e.read().decode()
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def api_get(path):
url = API.rstrip("/") + path
try:
resp = urllib.request.urlopen(urllib.request.Request(url), timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def api_get(path):
url = API.rstrip("/") + path
try:
resp = urllib.request.urlopen(urllib.request.Request(url), timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="POST",
)
try:
resp = urllib.request.urlopen(req, timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="POST",
)
try:
resp = urllib.request.urlopen(req, timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="POST",
)
try:
resp = urllib.request.urlopen(req, timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="POST",
)
try:
resp = urllib.request.urlopen(req, timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers={"Content-Type": "application/json"} if data else {},
)
try:
resp = urllib.request.urlopen(req, timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers={"Content-Type": "application/json"} if data else {},
)
try:
resp = urllib.request.urlopen(req, timeout=10)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def api_get(path):
url = API.rstrip("/") + path
try:
resp = urllib.request.urlopen(urllib.request.Request(url), timeout=15)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def api_get(path):
url = API.rstrip("/") + path
try:
resp = urllib.request.urlopen(urllib.request.Request(url), timeout=15)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="POST",
)
try:
resp = urllib.request.urlopen(req, timeout=15)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
method="POST",
)
try:
resp = urllib.request.urlopen(req, timeout=15)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
req = urllib.request.Request(url, data=b"", method="POST",
headers={"Content-Type": "application/json"})
try:
resp = urllib.request.urlopen(req, timeout=30)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
req = urllib.request.Request(url, data=b"", method="POST",
headers={"Content-Type": "application/json"})
try:
resp = urllib.request.urlopen(req, timeout=30)
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"HTTP {e.code}: {e.read().decode()}", file=sys.stderr)
The description promises a broad email skill for standard IMAP/SMTP-based personal email management, including sending mail, checking inboxes, searching, mailbox listing, attachment saving, scheduling emails, and push notifications. The supplied code instead implements a narrow inbox-grouping utility. It analyzes messages via HTTP endpoints, suggests recurring sender/subject groups, applies moves into subfolders, and manages auto-grouping state. While moving messages between folders is loosely related to email management, the primary behavior is materially different from the declared general email functionality, and several prominent declared capabilities are absent from this code chunk. Therefore the description does not accurately represent what this code actually does.
No suspicious patterns detected.