Xinference PyPI 2.6.x Import-Time Credential Exfiltration
JFrog reported that the legitimate PyPI package xinference shipped malicious versions 2.6.0, 2.6.1, and 2.6.2 with import-time code in xinference/__init__.py. The payload collected host and secret material into love.tar.gz and posted it to whereisitat.lucyatemysuperbox.space with header X-QT-SR: 14.
On this page 0% read
Executive Summary
JFrog reported a PyPI supply-chain compromise of the legitimate xinference package affecting versions 2.6.0, 2.6.1, and 2.6.2. The malicious code lived in xinference/__init__.py, so exposure requires installation plus Python import, CLI startup, service startup, or another path that loads the package [Source 1].
The decoded payload created a temporary directory, ran a second-stage collector, captured output to a temporary file named f, compressed it into love.tar.gz, and uploaded it to https://whereisitat.lucyatemysuperbox.space/ using curl --data-binary and header X-QT-SR: 14 [Source 1]. JFrog lists the package file hash, stage hashes, archive name, actor marker string, affected versions, and Xray ID XRAY-96896 [Source 1].
Current PyPI release history shows 2.5.0 before 2.7.0; the malicious 2.6.x releases are no longer in the visible history. Use version selectors and package-cache evidence as the primary exposure proof, then use C2/domain/header/hash selectors to separate confirmed execution from presumed exposure [Source 2].
Key Facts
event_type: "legitimate PyPI package compromise"
ecosystem: "PyPI"
package:
name: "xinference"
malicious_versions:
- "2.6.0"
- "2.6.1"
- "2.6.2"
visible_current_pypi_gap: "2.5.0 to 2.7.0"
collection_window_utc:
start: "2026-04-22T00:00:00Z"
end: "2026-04-23T23:59:59Z"
execution_trigger: "Python import or service/CLI path loading xinference/__init__.py"
malicious_file:
path: "xinference/__init__.py"
sha256: "e1e007ce4eab7774785617179d1c01a9381ae83abfd431aae8dba6f82d3ac127"
decoded_stage_hashes_sha256:
stage_1: "077d49fa708f498969d7cdffe701eb64675baaa4968ded9bd97a4936dd56c21c"
stage_2: "fe17e2ea4012d07d90ecb7793c1b0593a6138d25a9393192263e751660ec3cd0"
network_iocs:
- "whereisitat.lucyatemysuperbox.space"
- "https://whereisitat.lucyatemysuperbox.space/"
protocol_artifacts:
- "curl --data-binary"
- "X-QT-SR: 14"
file_artifacts:
- "love.tar.gz"
- "f"
strings:
- "# hacked by teampcp"
credentials_at_risk:
- "environment variables"
- "SSH keys"
- "cloud credentials"
- "Kubernetes tokens and configs"
- "Docker registry credentials"
- "PyPI/npm/Cargo publishing tokens"
- ".env secrets"
- "TLS private keys and certificates"
- "database credentials"
- "wallet keys and seed material"
Source Confidence & Evidence Mapping
- confirmed: JFrog identified
xinferenceversions2.6.0,2.6.1, and2.6.2as compromised and lists Xray IDXRAY-96896[Source 1]. - confirmed: JFrog identified
xinference/__init__.pyas the malicious file, SHA-256e1e007ce4eab7774785617179d1c01a9381ae83abfd431aae8dba6f82d3ac127, and decoded stage SHA-256 values077d49fa708f498969d7cdffe701eb64675baaa4968ded9bd97a4936dd56c21candfe17e2ea4012d07d90ecb7793c1b0593a6138d25a9393192263e751660ec3cd0[Source 1]. - confirmed: JFrog observed exfiltration to
https://whereisitat.lucyatemysuperbox.space/, archive namelove.tar.gz, and HTTP headerX-QT-SR: 14[Source 1]. - unclear: Public evidence does not prove the exact PyPI publish credential theft path. JFrog describes a likely path based on prior campaign patterns, but the specific initial access vector remains unconfirmed [Source 1].
- not_observed: JFrog states the payload does not include persistence, a reverse shell, destructive wiping, ransomware, or privilege escalation [Source 1].
Impact Determination
| Classification | Criteria | Evidence to collect | Handling decision |
|---|---|---|---|
| Confirmed compromise | xinference==2.6.0, xinference==2.6.1, or xinference==2.6.2 was installed and import/runtime/C2 evidence exists. | Installed package metadata, lockfile, package cache, Python import traces, xinference/__init__.py hash, love.tar.gz, C2 DNS/HTTP, X-QT-SR: 14, # hacked by teampcp. | Preserve the host/container/runner evidence, isolate the asset, revoke credentials reachable from the Python process, and run the downstream audits below. |
| Presumed exposed | An affected version was installed in a host, image, notebook, virtualenv, or CI job, but import/network telemetry is missing. | pip freeze, requirements*.txt, poetry.lock, uv.lock, image layer output, CI install output, package cache. | Treat secret material readable by that environment as exposed unless import non-execution can be proven. |
| Potentially exposed | xinference was unpinned or upgraded during the April 22-23 collection window, but resolved version evidence is incomplete. | Dependency manifests, package proxy records, CI output, image build history, notebook environment exports. | Collect resolver/cache evidence before reducing scope. |
| Not exposed | Evidence shows no malicious 2.6.x version in source, lockfiles, package caches, images, hosts, or runtime telemetry. | Negative dependency inventory, virtualenv/container search, package cache query, and network selector search. | Keep negative evidence with the case record and close this event for that asset. |
| Unknown | Required package inventory, CI output, endpoint telemetry, image inventory, or network telemetry is unavailable. | Named telemetry gap with system, owner, and retention status. | Keep high-value AI/ML, GPU, CI, and cloud-adjacent assets in scope until evidence is recovered or risk is explicitly accepted. |
Minimum Evidence To Collect
package_evidence:
- "xinference==2.6.0"
- "xinference==2.6.1"
- "xinference==2.6.2"
- "xinference/__init__.py SHA-256 e1e007ce4eab7774785617179d1c01a9381ae83abfd431aae8dba6f82d3ac127"
runtime_evidence:
- "Python import of xinference"
- "subprocess.Popen child Python execution"
- "curl --data-binary"
network_evidence:
- "whereisitat.lucyatemysuperbox.space"
- "https://whereisitat.lucyatemysuperbox.space/"
- "X-QT-SR: 14"
file_evidence:
- "love.tar.gz"
- "temporary collector output file named f"
string_evidence:
- "# hacked by teampcp"
Timeline
- 2026-04-22: JFrog published research identifying
xinferenceversions2.6.0,2.6.1, and2.6.2as compromised and yanked [Source 1]. - 2026-04-22: JFrog described the malicious payload in
xinference/__init__.py, including import-time execution and staged collection/exfiltration [Source 1]. - 2026-04-22: JFrog listed the IOCs: affected package versions, C2 domain/URL, header
X-QT-SR: 14, SHA-256 hashes, archivelove.tar.gz, and marker# hacked by teampcp[Source 1]. - 2026-04-25: PyPI visible release history resumed at
2.7.0after2.5.0, leaving the compromised2.6.xreleases absent from the current visible release list [Source 2].
What Happened
Attackers published malicious xinference versions directly to PyPI under the legitimate package name. JFrog reports that this was not a lookalike package; the affected identity was the real xinference package [Source 1].
The malicious code was placed in xinference/__init__.py, making import and service startup the relevant execution boundary. The first stage decoded a second-stage collector, ran it in a child Python interpreter, wrote collected output to a temporary file named f, compressed that file into love.tar.gz, and uploaded the archive with curl to the C2 URL [Source 1].
JFrog’s decoded collector logic targeted host inventory and secret locations. The article specifically calls out SSH material, cloud credentials, Kubernetes service account tokens/configs, Docker registry credentials, package publishing tokens, .env secrets, TLS material, database passwords, and wallet key material as credential classes requiring downstream scoping [Source 1].
Technical Analysis
Package Manipulation
package_identity:
registry: "PyPI"
package: "xinference"
malicious_versions:
- "2.6.0"
- "2.6.1"
- "2.6.2"
malicious_file:
path: "xinference/__init__.py"
sha256: "e1e007ce4eab7774785617179d1c01a9381ae83abfd431aae8dba6f82d3ac127"
execution_trigger: "module import or startup path that imports xinference"
Payload Behavior
The stage-one payload created temporary working storage, decoded and piped a second-stage collector into a child Python interpreter, captured stdout into f, compressed the output into love.tar.gz, and sent the archive using curl --data-binary [Source 1].
Exfiltration
exfiltration:
domain: "whereisitat.lucyatemysuperbox.space"
url: "https://whereisitat.lucyatemysuperbox.space/"
method: "HTTP POST"
tool: "curl"
body_mode: "--data-binary"
custom_header: "X-QT-SR: 14"
archive: "love.tar.gz"
Affected Assets and Blast Radius
affected_assets:
ecosystems:
- "PyPI"
packages:
- "xinference==2.6.0"
- "xinference==2.6.1"
- "xinference==2.6.2"
environments:
- "developer virtualenvs"
- "Jupyter notebook kernels"
- "GPU inference servers"
- "CI runners"
- "container images built from affected requirements"
- "Kubernetes pods importing xinference"
downstream_systems:
- "AWS"
- "GCP"
- "Azure"
- "Kubernetes"
- "Docker registries"
- "PyPI/npm/Cargo registries"
- "Git hosting"
not_currently_known_to_affect:
- "visible PyPI releases 2.5.0 and 2.7.0 when installed from official current metadata"
Indicators of Compromise
package_versions:
- "xinference==2.6.0"
- "xinference==2.6.1"
- "xinference==2.6.2"
files:
- "xinference/__init__.py"
- "love.tar.gz"
- "f"
hashes_sha256:
- "e1e007ce4eab7774785617179d1c01a9381ae83abfd431aae8dba6f82d3ac127"
- "077d49fa708f498969d7cdffe701eb64675baaa4968ded9bd97a4936dd56c21c"
- "fe17e2ea4012d07d90ecb7793c1b0593a6138d25a9393192263e751660ec3cd0"
domains:
- "whereisitat.lucyatemysuperbox.space"
urls:
- "https://whereisitat.lucyatemysuperbox.space/"
process_patterns:
- "curl --data-binary"
- "subprocess.Popen"
headers:
- "X-QT-SR: 14"
strings:
- "# hacked by teampcp"
Detection and Hunting
Script: local repository and exported telemetry scope
#!/usr/bin/env python3
import os
import sys
import json
import subprocess
from pathlib import Path
ROOT = sys.argv[1] if len(sys.argv) > 1 else "."
LOG_ROOT = os.environ.get("LOG_ROOT", "")
OUT = Path(os.environ.get("OUT", "hp-xinference-pypi-credential-hijack-scope"))
SINCE = "2026-04-22T00:00:00Z"
UNTIL = "2026-04-23T23:59:59Z"
PACKAGES = [
]
VERSIONS = [
"xinference==2.6.0",
"xinference==2.6.1",
"xinference==2.6.2",
]
FILES = [
"xinference/__init__.py",
"love.tar.gz",
"f",
]
DOMAINS = [
"whereisitat.lucyatemysuperbox.space",
"love.tar.gz",
]
URLS = [
"https://whereisitat.lucyatemysuperbox.space/",
]
IPS = [
]
HASHES = [
"e1e007ce4eab7774785617179d1c01a9381ae83abfd431aae8dba6f82d3ac127",
"077d49fa708f498969d7cdffe701eb64675baaa4968ded9bd97a4936dd56c21c",
"fe17e2ea4012d07d90ecb7793c1b0593a6138d25a9393192263e751660ec3cd0",
]
PROCESS_PATTERNS = [
"curl --data-binary",
"subprocess.Popen",
]
NETWORK_PATTERNS = [
]
# Positive signal: repository, lockfile, artifact, process, or network telemetry contains one of the exact incident selectors above.
# Escalation: any match tied to a production build, CI run, deployed asset, or secret-bearing host moves the asset to presumed exposed.
OUT.mkdir(parents=True, exist_ok=True)
indicators_file = OUT / "indicators.txt"
# Collect unique indicators
indicators = set()
for group in [PACKAGES, VERSIONS, FILES, DOMAINS, URLS, IPS, HASHES, PROCESS_PATTERNS, NETWORK_PATTERNS]:
for val in group:
if val:
indicators.add(val)
with open(indicators_file, "w") as f:
for ind in sorted(indicators):
f.write(ind + "\n")
print(f"[+] Written unique selectors to {indicators_file}")
# Walk local directory
print(f"[+] Scanning directory: {ROOT} for selectors...")
matches = []
exclude_dirs = {"node_modules", "vendor", "dist", ".git"}
for root, dirs, filenames in os.walk(ROOT):
dirs[:] = [d for d in dirs if d not in exclude_dirs]
for filename in filenames:
filepath = Path(root) / filename
try:
content = filepath.read_text(errors="ignore")
for ind in indicators:
if ind in content:
matches.append(f"{filepath}: found '{ind}'")
except Exception:
pass
if matches:
(OUT / "repository-indicator-matches.txt").write_text("\n".join(matches) + "\n")
print(f"[!] Found {len(matches)} matches in codebase!")
# Optional Log Scanning
if LOG_ROOT and os.path.exists(LOG_ROOT):
print(f"[+] Scanning telemetry log directory: {LOG_ROOT}...")
log_matches = []
for root, _, filenames in os.walk(LOG_ROOT):
for filename in filenames:
filepath = Path(root) / filename
try:
content = filepath.read_text(errors="ignore")
for ind in indicators:
if ind in content:
log_matches.append(f"{filepath}: found '{ind}'")
except Exception:
pass
if log_matches:
(OUT / "exported-telemetry-indicator-matches.txt").write_text("\n".join(log_matches) + "\n")
print(f"[!] Found {len(log_matches)} matches in logs!")
if PACKAGES:
registry_dir = OUT / "registry"
registry_dir.mkdir(exist_ok=True)
for package in PACKAGES:
if not package: continue
safe_name = package.replace("/", "__")
print(f"[+] Querying pip index for {package}...")
res = subprocess.run(["python3", "-m", "pip", "index", "versions", package], capture_output=True, text=True)
if res.returncode == 0:
(registry_dir / f"pypi-{safe_name}-versions.txt").write_text(res.stdout)
subprocess.run(["python3", "-m", "pip", "download", "--no-deps", package, "-d", str(registry_dir)], capture_output=True)
print(f"[+] Wrote scope artifacts under {OUT}")
Downstream Abuse Audits
Script: GitHub organization run, release, secret, and workflow audit
#!/usr/bin/env python3
import os
import sys
import json
import subprocess
from pathlib import Path
if "ORG" not in os.environ:
print("ERROR: Set ORG environment variable to the GitHub organization to audit", file=sys.stderr)
sys.exit(1)
ORG = os.environ["ORG"]
SINCE = "2026-04-22T00:00:00Z"
UNTIL = "2026-04-23T23:59:59Z"
OUT = Path(os.environ.get("OUT", "hp-xinference-pypi-credential-hijack-github-audit"))
SELECTORS = [
"xinference==2.6.0",
"xinference==2.6.1",
"xinference==2.6.2",
"xinference/__init__.py",
"love.tar.gz",
"f",
"whereisitat.lucyatemysuperbox.space",
"https://whereisitat.lucyatemysuperbox.space/",
"e1e007ce4eab7774785617179d1c01a9381ae83abfd431aae8dba6f82d3ac127",
"077d49fa708f498969d7cdffe701eb64675baaa4968ded9bd97a4936dd56c21c",
"fe17e2ea4012d07d90ecb7793c1b0593a6138d25a9393192263e751660ec3cd0",
]
# Positive signal: a workflow run, release, secret, key, package, or workflow change overlaps the exposure window and references an incident selector.
# Remediation trigger: unauthorized post-exposure write activity or a secret-bearing run matching an incident selector requires token revocation and downstream cloud/registry review.
OUT.mkdir(parents=True, exist_ok=True)
(OUT / "runs").mkdir(exist_ok=True)
(OUT / "logs").mkdir(exist_ok=True)
(OUT / "repos").mkdir(exist_ok=True)
# 1. Write incident-selectors file
selectors_file = OUT / "incident-selectors.txt"
with open(selectors_file, "w") as sf:
for s in SELECTORS:
if s:
sf.write(s + "\n")
# 2. Get list of repos
print(f"[+] Fetching repositories for organization: {ORG}")
repo_res = subprocess.run(["gh", "repo", "list", ORG, "--limit", "1000", "--json", "nameWithOwner"], capture_output=True, text=True)
if repo_res.returncode != 0:
print(f"[-] Failed to fetch repos: {repo_res.stderr}", file=sys.stderr)
sys.exit(1)
repos = [r["nameWithOwner"] for r in json.loads(repo_res.stdout)]
for repo in repos:
safe_repo = repo.replace("/", "__")
print(f"[+] Auditing repository: {repo}")
# Check runs in the window
runs_res = subprocess.run([
"gh", "api", f"/repos/{repo}/actions/runs",
"-f", "per_page=100",
"-f", f"created=>={SINCE}",
"--paginate"
], capture_output=True, text=True)
if runs_res.returncode == 0:
try:
all_runs = json.loads(runs_res.stdout).get("workflow_runs", [])
filtered_runs = [r for r in all_runs if r["created_at"] <= UNTIL]
if filtered_runs:
with open(OUT / "runs" / f"{safe_repo}-runs.jsonl", "w") as rf:
for run in filtered_runs:
rf.write(json.dumps(run) + "\n")
# Fetch log dynamically
run_id = str(run["id"])
log_res = subprocess.run(["gh", "run", "view", run_id, "--repo", repo, "--log"], capture_output=True, text=True)
if log_res.returncode == 0:
(OUT / "logs" / f"{safe_repo}-{run_id}.log").write_text(log_res.stdout)
# Fetch details
view_res = subprocess.run(["gh", "run", "view", run_id, "--repo", repo, "--json", "databaseId,workflowName,headSha,event,createdAt,jobs"], capture_output=True, text=True)
if view_res.returncode == 0:
(OUT / "runs" / f"{safe_repo}-{run_id}.json").write_text(view_res.stdout)
except Exception as e:
print(f"[-] Error parsing runs for {repo}: {e}")
# Check releases in window
subprocess.run(["gh", "api", f"/repos/{repo}/releases", "-f", "per_page=100", "--paginate"], capture_output=True)
# Check repo secrets updated in window
subprocess.run(["gh", "api", f"/repos/{repo}/actions/secrets", "-f", "per_page=100", "--paginate"], capture_output=True)
# Check deploy keys
subprocess.run(["gh", "api", f"/repos/{repo}/keys", "-f", "per_page=100", "--paginate"], capture_output=True)
# Scan output directory for any indicator selector matches
print("[+] Scanning gathered telemetry for indicator matches...")
subprocess.run(["rg", "-n", "--hidden", "--fixed-strings", "-f", str(selectors_file), str(OUT)], capture_output=False)
print(f"[+] Wrote GitHub audit artifacts under {OUT}")
Script: cloud OIDC and deployment credential follow-on audit
#!/usr/bin/env python3
import os
import json
import subprocess
from pathlib import Path
SINCE = "2026-04-22T00:00:00Z"
UNTIL = "2026-04-23T23:59:59Z"
OUT = Path(os.environ.get("OUT", "hp-xinference-pypi-credential-hijack-cloud-audit"))
AWS_REGIONS = os.environ.get("AWS_REGIONS", "us-east-1").split(",")
# Positive signal: token exchange or privileged write activity occurs in the exposure window from GitHub, CI/CD, package registry, or deployment automation identity.
# Remediation trigger: unexpected write, deploy, IAM, secret, or registry activity tied to an exposed CI/CD path requires trust-policy disablement and credential rotation.
OUT.mkdir(parents=True, exist_ok=True)
# 1. AWS CloudTrail Audit
print("[+] Querying AWS CloudTrail for Web Identity token exchanges...")
aws_events = []
for region in AWS_REGIONS:
res = subprocess.run([
"aws", "cloudtrail", "lookup-events",
"--region", region,
"--start-time", SINCE,
"--end-time", UNTIL,
"--lookup-attributes", "AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity",
"--output", "json"
], capture_output=True, text=True)
if res.returncode == 0:
try:
events = json.loads(res.stdout).get("Events", [])
for event in events:
ct = json.loads(event.get("CloudTrailEvent", "{}"))
ct["region"] = region
aws_events.append(ct)
except Exception as e:
print(f"[-] Error parsing AWS CloudTrail events: {e}")
if aws_events:
with open(OUT / "aws-assume-role-with-web-identity.jsonl", "w") as f:
for ev in aws_events:
f.write(json.dumps(ev) + "\n")
# Audit follow-on events for returned access keys
for ev in aws_events:
access_key = ev.get("responseElements", {}).get("credentials", {}).get("accessKeyId")
region = ev.get("region", "us-east-1")
if access_key:
print(f"[+] Enumerating AWS events for AccessKey: {access_key}")
f_res = subprocess.run([
"aws", "cloudtrail", "lookup-events",
"--region", region,
"--start-time", SINCE,
"--end-time", UNTIL,
"--lookup-attributes", f"AttributeKey=AccessKeyId,AttributeValue={access_key}",
"--output", "json"
], capture_output=True, text=True)
if f_res.returncode == 0:
try:
f_events = json.loads(f_res.stdout).get("Events", [])
with open(OUT / "aws-follow-on-api-calls.jsonl", "a") as ff:
for fe in f_events:
ff.write(fe.get("CloudTrailEvent", "{}") + "\n")
except Exception as e:
print(f"[-] Error writing follow-on events: {e}")
# 2. Azure Activity Log Audit
print("[+] Querying Azure activity logs...")
az_res = subprocess.run([
"az", "monitor", "activity-log", "list",
"--start-time", SINCE,
"--end-time", UNTIL,
"--query", "[?contains(operationName.value, 'write') || contains(operationName.value, 'delete') || contains(operationName.value, 'Microsoft.ManagedIdentity')]",
"-o", "json"
], capture_output=True, text=True)
if az_res.returncode == 0:
(OUT / "azure-write-delete-activity.json").write_text(az_res.stdout)
# 3. GCP Logging Audit
print("[+] Querying GCP Cloud Logging...")
gcp_filter = f'timestamp>="{SINCE}" AND timestamp<="{UNTIL}" AND (protoPayload.methodName="google.sts.v1.SecurityTokenService.ExchangeToken" OR protoPayload.methodName:"GenerateAccessToken" OR protoPayload.methodName:"CreateServiceAccountKey" OR protoPayload.methodName:"SetIamPolicy")'
gcp_res = subprocess.run([
"gcloud", "logging", "read", gcp_filter,
"--format", "json"
], capture_output=True, text=True)
if gcp_res.returncode == 0:
(OUT / "gcp-token-and-iam-activity.json").write_text(gcp_res.stdout)
print(f"[+] Wrote cloud audit artifacts under {OUT}")
Script: registry metadata and artifact audit
#!/usr/bin/env python3
import os
import json
import subprocess
from pathlib import Path
SINCE = "2026-04-22T00:00:00Z"
OUT = Path(os.environ.get("OUT", "hp-xinference-pypi-credential-hijack-registry-audit"))
PACKAGES = [
]
VERSIONS = [
"xinference==2.6.0",
"xinference==2.6.1",
"xinference==2.6.2",
]
# Positive signal: registry metadata, package tarballs, or cached artifacts contain the exact affected package/version values.
# Remediation trigger: any internal package cache, build artifact, or deployment using these package/version values requires exposure scoping.
OUT.mkdir(parents=True, exist_ok=True)
with open(OUT / "affected-versions.txt", "w") as av:
for version in VERSIONS:
if version:
av.write(version + "\n")
# 1. Audit PyPI dependencies in project files
print("[+] Scanning PyPI dependency files...")
for file in ["requirements.txt", "poetry.lock", "Pipfile.lock", "pyproject.toml", "setup.py"]:
if Path(file).exists():
subprocess.run(["rg", "-n", "--hidden", "--fixed-strings", "-f", str(OUT / "affected-versions.txt"), file])
# 2. Query registry metadata and download packages for local analysis
packages_dir = OUT / "packages"
metadata_dir = OUT / "metadata"
packages_dir.mkdir(exist_ok=True)
metadata_dir.mkdir(exist_ok=True)
for package in PACKAGES:
if not package: continue
print(f"[+] Querying pip index for {package}...")
res = subprocess.run(["python3", "-m", "pip", "index", "versions", package], capture_output=True, text=True)
if res.returncode == 0:
(metadata_dir / f"{package}-versions.txt").write_text(res.stdout)
subprocess.run(["python3", "-m", "pip", "download", "--no-deps", package, "-d", str(packages_dir)], capture_output=True)
# 3. HOW TO REVOKE AND ROTATE EXPOSED PYPI PUBLISHING TOKENS:
# PyPI does not support token revocation via CLI. Follow these exact steps:
# 1. Log in to https://pypi.org/manage/account/
# 2. Scroll to the "API tokens" section and click "Remove" on any compromised tokens.
# 3. Generate a new API token limited to the specific project scope.
# 4. Update your CI/CD secrets using the GitHub CLI:
# subprocess.run(["gh", "secret", "set", "PYPI_API_TOKEN", "--body", "pypi-AgEIcHlwaS5vcm...", "--repo", "my-org/my-repo"])
print(f"[+] Wrote registry audit artifacts under {OUT}")
Sources
- JFrog Security Research: TeamPCP strikes again: Xinference PyPI package compromised - Role: PRIMARY_RESEARCH - Impact: Affected versions, malicious file, payload behavior, IOCs, hashes, C2, header, archive name, and non-persistence notes.
- PyPI: xinference release history - Role: REGISTRY_METADATA - Impact: Current visible project metadata and release-history gap around
2.6.x.
IOC Clipboard
9 IOCswhereisitat.lucyatemysuperbox.space whereisitat[.]lucyatemysuperbox[.]space love.tar.gz love[.]tar[.]gz https://whereisitat.lucyatemysuperbox.space/ hxxps://whereisitat[.]lucyatemysuperbox[.]space/ e1e007ce4eab7774785617179d1c01a9381ae83abfd431aae8dba6f82d3ac127 e1e007ce4eab7774785617179d1c01a9381ae83abfd431aae8dba6f82d3ac127 077d49fa708f498969d7cdffe701eb64675baaa4968ded9bd97a4936dd56c21c 077d49fa708f498969d7cdffe701eb64675baaa4968ded9bd97a4936dd56c21c fe17e2ea4012d07d90ecb7793c1b0593a6138d25a9393192263e751660ec3cd0 fe17e2ea4012d07d90ecb7793c1b0593a6138d25a9393192263e751660ec3cd0 xinference/__init__.py xinference/__init__.py love.tar.gz love.tar.gz f f