Highlights:
- PiPI is one of the largest Indexes, with more than 800,000 users
- Check Point CloudGuard identified a typosquatting campaign on PyPI, comprising over 500 malicious packages.
- Installation of these packages exposed users to potential theft of their personally identifiable information (PII) and the installation of malware on their systems.
- Upon detection, we promptly notified PyPI about these packages, leading to their swift removal by the PyPI administrative team.
Intro:
With more than 800,000 users, PyPI (Python Package Index) serves as the official repository for software packages tailored to the Python programming language. As a centralized hub, it facilitates the discovery, installation, and sharing of open-source Python packages among developers worldwide. Operated by the Python Software Foundation (PSF), PyPI is easily accessible through the pip package installer and hosts a vast array of packages, spanning from scientific computing libraries to web development frameworks and machine learning tools. The inclusive nature of PyPI allows anyone to contribute packages that meet specific requirements and guidelines outlined by the PSF, and it has evolved into an essential resource within the Python community, fostering collaboration and knowledge exchange.
Unfortunately, PyPI has increasingly become a target for malicious activities, with threat actors uploading packages designed to compromise unsuspecting users. These malicious packages typically consist of three key elements: 1) the malicious code itself, 2) the strategy employed for attack (such as leveraging PyPI’s dynamic features to inject malicious code during installation process), and 3) the method of infection (the reason why PyPI users will install such malicious packages in the first place).
Typosquatting is one of the most common infection techniques., and involves creating packages with names closely resembling popular ones, capitalizing on users’ potential typing errors. These typos can range from simple misspellings (requestss instead of requests) to more conceptual (pandas-sdk instead of pandas) or visually (request5 instead of requests) deceptive variations. The recent attack we’ve encountered encompassed various forms of typosquatting techniques, highlighting the evolving nature of threats in the digital landscape. With the emergence of Large Language Models (LLMs), we anticipate witnessing a broader spectrum of sophisticated attacks in the future. This incident serves as a poignant example of this evolving threat landscape, underscoring the proactive measures required to safeguard users from emerging risks posed by malicious actors.
The attack:
The typosquatting campaign we detected comprised over 500 malicious packages, deployed in two distinct waves on PyPI. Initially, approximately 200 packages were introduced, followed by an additional batch of more than 300. Each package originated from a unique maintainer account featuring distinct metadata such as name and email. Notably, each maintainer account uploaded only one package, indicating the utilization of automation in orchestrating the attack. This underscores the persistent challenge posed by determined attackers, who adeptly circumvent platform restrictions despite efforts by PyPI to fortify its defenses. The decentralized nature of the uploads, with each package attributed to a different user, complicates efforts to cross-identify these malicious entries. Accounts associated with the campaign were established on March 26th, with the malicious packages swiftly uploaded the following day, likely as a camouflage tactic to evade detection by heuristic malware scanning mechanisms.
Notably, despite the evident use of automation, the attackers did not invest significant effort into diversifying the details of the malicious packages. All entries shared identical version numbers (1.0.0) and contained the same malicious code. Additionally, the typosquatting names appeared to have been generated through a randomization process, evident in the simplistic variations employed (e.g., ‘reqjuests’ or ‘tensoflom’). Such variations may not easily deceive typical users compared to more plausible typos like ‘rrequests’, which capitalize on common keyboard mistakes. One plausible explanation is that these packages were intended to exploit users through code imports rather than relying on users’ typo mistakes.
The malicious packages didn’t include any code apart from the malicious script embedded within the package’s setup.py installation script. Upon confirmation that the installer’s operating system was Windows, this script would trigger the execution of obfuscated code.
Upon decryption, the obfuscated code revealed a straightforward command: to download and execute code from funcaptcha[.]ru, a newly registered domain likely created for this specific attack. It’s worth noting that, according to VirusTotal, several vendors had already flagged this domain as malicious.
Upon examination of the downloaded script, it contained yet another obfuscated segment, which was executed before being saved to the installer machine’s file system.
After de-obfuscation, the downloaded script revealed an extensive, well-documented malicious script, presenting two primary functionalities:
1. PII Stealer: harvest passwords and private files from the infected system.
2. Malicious Crypto Injectors: replacing existing cryptographic software with what appeared to be malicious counterparts.
Notably, the command and control server responsible for both delivering the malicious code and receiving stolen data was funcaptcha[.]ru. Below are some noteworthy excerpts from the malicious script.
Disclosure:
Shortly after the malicious packages were uploaded to PyPI, our automated machine-learning models promptly detected and alerted us to their presence. Subsequently, we disclosed the identified malicious packages to PyPI, leading to their swift removal by the PyPI administrative team.
An escalating risk:
It is crucial to reiterate the inherent risk associated with open-source components; there are no guarantees that the open-source tools are benign, emphasizing the need for thorough verification. With supply chain attacks on the rise, it becomes imperative to prioritize security measures to safeguard against potential threats. Vigilance is key—double-checking every software component, particularly those not developed in-house, is essential for mitigating risks effectively.
At Check Point, our mission is to foster a secure development environment, ensuring that developers adhere to best security practices. As part of our commitment, we continuously monitor repositories like PyPI and NPM for malicious packages to mitigate the risk of supply chain attacks. By leveraging the Check Point Infinity platform and it’s code security functionality, you can proactively protect your systems, ensuring you stay ahead of emerging threats posed by malicious actors.
To see if your organization was impacted, below is the full list of malicious packages that were discovered:
aasyncio@1.0.0 assyncio@1.0.0 asyincio@1.0.0 asyncci@1.0.0 asynccio@1.0.0 asynci@1.0.0 asyncii@1.0.0 asynciio@1.0.0 asyncioi@1.0.0 asyncioo@1.0.0 asynciooo@1.0.0 asynio@1.0.0 asynncio@1.0.0 asyyncio@1.0.0 aysncio@1.0.0 beaitifulsoop@1.0.0 beaitifulsoup@1.0.0 beaotifulsoup@1.0.0 beaufifulsoup@1.0.0 beaurifulsoup@1.0.0 beautifilsoop@1.0.0 beautifilsoup@1.0.0 beautiflulsoop@1.0.0 beautiflulsoup@1.0.0 beautifolsoup@1.0.0 beautifoulsoup@1.0.0 beautifuklsoup@1.0.0 beautifuksoup@1.0.0 beautifullsoop@1.0.0 beautifullsooup@1.0.0 beautifulsoop@1.0.0 beautifulsoul@1.0.0 beautifulsoupe@1.0.0 beautifulsoupo@1.0.0 beautifuosoup@1.0.0 beautilfulsoup@1.0.0 beautyfulsoup@1.0.0 beautysoup@1.0.0 beuatiflsoup@1.0.0 beutifullsoup@1.0.0 beutifulsoop@1.0.0 bibp-utils@1.0.0 biip-utils@1.0.0 bip-u8ls@1.0.0 bip-uils@1.0.0 bip-uitls@1.0.0 bip-util@1.0.0 bip-utilds@1.0.0 bip-utile@1.0.0 bip-utiles@1.0.0 bip-utilos@1.0.0 bip-utilss@1.0.0 bip-utilz@1.0.0 bip-utisl@1.0.0 bip-utjls@1.0.0 bip-utlils@1.0.0 bip-uttils@1.0.0 bip-uutils@1.0.0 bipp-utils@1.0.0 bips-utils@1.0.0 biup-utils@1.0.0 bop-utils@1.0.0 bpi-utils@1.0.0 bup-utils@1.0.0 bupi-utils@1.0.0 capmoneercloudclient@1.0.0 capmonsstercloudcliennt@1.0.0 capmonsstercloudclient@1.0.0 capmonsterccloudclient@1.0.0 capmonsterclouclient@1.0.0 capmonstercloudclenet@1.0.0 capmonstercloudclenit@1.0.0 capmonstercloudclent@1.0.0 capmonstercloudcliant@1.0.0 capmonstercloudclieent@1.0.0 capmonstercloudclieet@1.0.0 capmonstercloudclien@1.0.0 capmonstercloudcliend@1.0.0 capmonstercloudcliendt@1.0.0 capmonstercloudclienet@1.0.0 capmonstercloudcliennt@1.0.0 capmonstercloudclientt@1.0.0 capmonstercloudcliet@1.0.0 capmonstercloudcliient@1.0.0 capmonstercloudclinent@1.0.0 capmonstercloudclinet@1.0.0 capmonstercloudclouidclient@1.0.0 capmonstercloudcluodclient@1.0.0 capmonsterclouddclient@1.0.0 capmonsterclouddlient@1.0.0 capmonsterclouidclient@1.0.0 capmonsterclouudclient@1.0.0 capmonstercludclient@1.0.0 capmonstercoudclient@1.0.0 capmonstercouldclient@1.0.0 capmonsterrcloudclient@1.0.0 capmosterclouclient@1.0.0 capmostercloudclieent@1.0.0 capmostercloudclienet@1.0.0 capmostercloudclient@1.0.0 capmostercloudclinet@1.0.0 cilorama@1.0.0 clolorama@1.0.0 cloroma@1.0.0 colaroma@1.0.0 colomara@1.0.0 colorahma@1.0.0 coloramae@1.0.0 coloramah@1.0.0 coloramal@1.0.0 coloramaz@1.0.0 colorame@1.0.0 coloramia@1.0.0 coloramka@1.0.0 coloramna@1.0.0 coloramo@1.0.0 coloramoo@1.0.0 coloramqa@1.0.0 coloramqs@1.0.0 coloramu@1.0.0 coloramwa@1.0.0 coloramws@1.0.0 coloramxa@1.0.0 coloramxs@1.0.0 coloramza@1.0.0 coloramzs@1.0.0 colorayma@1.0.0 colorhrama@1.0.0 colorm@1.0.0 colormma@1.0.0 coloroama@1.0.0 colorram@1.0.0 colorramma@1.0.0 colouorama@1.0.0 colprama@1.0.0 corlorama@1.0.0 cstmotkinter@1.0.0 cuatomtkinter@1.0.0 cusgtomtkinter@1.0.0 cusromtkinter@1.0.0 custm@1.0.0 custmtkinter@1.0.0 custmtokinter@1.0.0 custogtkinter@1.0.0 custohtkinter@1.0.0 custojmtkinter@1.0.0 custojtkinter@1.0.0 custoktkinter@1.0.0 customekinter@1.0.0 customkinter@1.0.0 customtikinter@1.0.0 customtiknter@1.0.0 customtinter@1.0.0 customtjinter@1.0.0 customtkfnter@1.0.0 customtkibter@1.0.0 customtkihter@1.0.0 customtkimter@1.0.0 customtkinber@1.0.0 customtkinet@1.0.0 customtkinetr@1.0.0 customtkinger@1.0.0 customtkingter@1.0.0 customtkinrer@1.0.0 customtkintar@1.0.0 customtkinte@1.0.0 customtkinted@1.0.0 customtkinteer@1.0.0 customtkintert@1.0.0 customtkintet@1.0.0 customtkintre@1.0.0 customtkintrer@1.0.0 customtkintrr@1.0.0 customtkintwr@1.0.0 customtkinyer@1.0.0 customtkitenr@1.0.0 customtkiter@1.0.0 customtkitner@1.0.0 customtkitnerr@1.0.0 customtkitnre@1.0.0 customtkiyter@1.0.0 customtkjnter@1.0.0 customtkknter@1.0.0 customtkniter@1.0.0 customtkniterr@1.0.0 customtknster@1.0.0 customtknter@1.0.0 customtkwnter@1.0.0 customtkznter@1.0.0 custontkinter@1.0.0 custoqtkinter@1.0.0 custotinter@1.0.0 custotkinter@1.0.0 custotkminter@1.0.0 custotminter@1.0.0 custoumtkinter@1.0.0 custpmtkinter@1.0.0 custrmtkinter@1.0.0 custumtkinter@1.0.0 custvomtkinter@1.0.0 cutomtkinter@1.0.0 cuwtomtkinter@1.0.0 cuxtomtkinter@1.0.0 insanepackage217234234242423442983@1.0.0 insanepackage21724342386744243242983@1.0.0 insanepackage2179824234242342433@1.0.0 insanepackageongong11192@1.0.0 insanepackageongong192@1.0.0 insanepackagev1414@1.0.0 johnhammondfanpackage124@1.0.0 johnhammondontop183@1.0.0 maptplotlib@1.0.0 matplftlib@1.0.0 matpliotlib@1.0.0 matplkotlib@1.0.0 matpllotb@1.0.0 matpllotib@1.0.0 matplolplib@1.0.0 matploltlab@1.0.0 matploltlib@1.0.0 matplootib@1.0.0 matploptlib@1.0.0 matplorlib@1.0.0 matplotblib@1.0.0 matplotib@1.0.0 matplotkib@1.0.0 matplotklib@1.0.0 matplotlbib@1.0.0 matplotlig@1.0.0 matplotllib@1.0.0 matplotlob@1.0.0 matplotlpib@1.0.0 matplotlr@1.0.0 matplotltib@1.0.0 matplotlub@1.0.0 matplotlyib@1.0.0 matplotoib@1.0.0 matplotpib@1.0.0 matplottbib@1.0.0 matplottib@1.0.0 matplottlab@1.0.0 matplotvib@1.0.0 matplotvlib@1.0.0 matplptlib@1.0.0 matplrtib@1.0.0 matplrtlib@1.0.0 matpltotlib@1.0.0 matplttlib@1.0.0 matplutlib@1.0.0 oillow@1.0.0 p-cord@1.0.0 p8llow@1.0.0 p9llow@1.0.0 pi-cord@1.0.0 pilkow@1.0.0 pill9w@1.0.0 pilliow@1.0.0 pilliw@1.0.0 pillkw@1.0.0 pillo2@1.0.0 pilloa@1.0.0 pilloo@1.0.0 pilloq@1.0.0 pillox@1.0.0 pilpow@1.0.0 piolow@1.0.0 piplow@1.0.0 pirlow@1.0.0 pjllow@1.0.0 plaawright@1.0.0 plauwright@1.0.0 plawwright@1.0.0 plawyright@1.0.0 playrwight@1.0.0 playwirght@1.0.0 playwrght@1.0.0 playwrgiht@1.0.0 playwrgith@1.0.0 playwrigght@1.0.0 playwrigh@1.0.0 playwrightt@1.0.0 playwrigth@1.0.0 playwrihgt@1.0.0 playwritgh@1.0.0 plyawright@1.0.0 plywright@1.0.0 pollow@1.0.0 pqtorch@1.0.0 pttorch@1.0.0 pullow@1.0.0 py-c0ard@1.0.0 py-c0crd@1.0.0 py-c0dd@1.0.0 py-c0red@1.0.0 py-c9rd@1.0.0 py-cdord@1.0.0 py-cird@1.0.0 py-ckord@1.0.0 py-ckrd@1.0.0 py-co4d@1.0.0 py-coad@1.0.0 py-cobrd@1.0.0 py-cocd@1.0.0 py-cod@1.0.0 py-codrd@1.0.0 py-coed@1.0.0 py-coerd@1.0.0 py-cofd@1.0.0 py-cofrd@1.0.0 py-coird@1.0.0 py-cojrd@1.0.0 py-coordd@1.0.0 py-coqrd@1.0.0 py-corad@1.0.0 py-cordd@1.0.0 py-corddd@1.0.0 py-corde@1.0.0 py-cordf@1.0.0 py-cordq@1.0.0 py-cordr@1.0.0 py-cordv@1.0.0 py-cordw@1.0.0 py-cordx@1.0.0 py-corf@1.0.0 py-corfd@1.0.0 py-corg@1.0.0 py-corid@1.0.0 py-corrd@1.0.0 py-cortd@1.0.0 py-corwd@1.0.0 py-corx@1.0.0 py-corxd@1.0.0 py-cotd@1.0.0 py-cotrd@1.0.0 py-cowrd@1.0.0 py-cozd@1.0.0 py-cpord@1.0.0 py-cprd@1.0.0 py-crd@1.0.0 py-crodd@1.0.0 py-cwrd@1.0.0 py-cxrd@1.0.0 py-cyrd@1.0.0 py-czrd@1.0.0 py-vord@1.0.0 py-xord@1.0.0 pycjrd@1.0.0 pycordde@1.0.0 pycordwd@1.0.0 pygacme@1.0.0 pygaeme@1.0.0 pygaime@1.0.0 pygamke@1.0.0 pygamm@1.0.0 pygamne@1.0.0 pygamr@1.0.0 pygamse@1.0.0 pygamw@1.0.0 pygane@1.0.0 pygaome@1.0.0 pygaqme@1.0.0 pygarme@1.0.0 pygawme@1.0.0 pygazme@1.0.0 pygfame@1.0.0 pygfme@1.0.0 pyghame@1.0.0 pygmme@1.0.0 pygqame@1.0.0 pygqme@1.0.0 pygume@1.0.0 pygvame@1.0.0 pygxme@1.0.0 pygzme@1.0.0 pytarch@1.0.0 pytbrch@1.0.0 pytcrch@1.0.0 pythrch@1.0.0 pytirch@1.0.0 pytlrc@1.0.0 pytoich@1.0.0 pytorbch@1.0.0 pytorcb@1.0.0 pytorcdh@1.0.0 pytorchb@1.0.0 pytorchc@1.0.0 pytorchg@1.0.0 pytorchj@1.0.0 pytorchv@1.0.0 pytorchy@1.0.0 pytorcm@1.0.0 pytorcu@1.0.0 pytordh@1.0.0 pytorqh@1.0.0 pytprch@1.0.0 pytroce@1.0.0 pytrosh@1.0.0 pzgame@1.0.0 pztorch@1.0.0 rensoflow@1.0.0 reqeist@1.0.0 reqeosts@1.0.0 reqeuste@1.0.0 reqeustx@1.0.0 reqeustz@1.0.0 reqeyst@1.0.0 reqirements@1.0.0 reqiremnets@1.0.0 reqiremnts@1.0.0 reqiurements@1.0.0 reqiurementstxt@1.0.0 reqiuremnets@1.0.0 reqjuests@1.0.0 reqoests@1.0.0 reqquest@1.0.0 reqsests@1.0.0 requas@1.0.0 requeits@1.0.0 requeksts@1.0.0 requekts@1.0.0 requeqsts@1.0.0 requesgt@1.0.0 requesks@1.0.0 requesqs@1.0.0 requesrts@1.0.0 requestr@1.0.0 requesuts@1.0.0 requesxs@1.0.0 requesxt@1.0.0 requesxts@1.0.0 requetsa@1.0.0 requetsq@1.0.0 requetsts@1.0.0 requewsts@1.0.0 requiements@1.0.0 requierement@1.0.0 requierments@1.0.0 requiirements@1.0.0 requiirementstx@1.0.0 requiirementstxt@1.0.0 requiirementsxt@1.0.0 requiiremments@1.0.0 requiiremnts@1.0.0 requiirments@1.0.0 requiremants@1.0.0 requiremeents@1.0.0 requiremenstx@1.0.0 requiremenstxt@1.0.0 requirementss@1.0.0 requirementst@1.0.0 requirementstt@1.0.0 requirementsttx@1.0.0 requirementstx@1.0.0 requirementstxtt@1.0.0 requirementstxtx@1.0.0 requirementstxtxt@1.0.0 requirementstxx@1.0.0 requirementstxxt@1.0.0 requirementt@1.0.0 requirementtsxt@1.0.0 requirementxstxt@1.0.0 requirementxt@1.0.0 requirementxtt@1.0.0 requirementxxt@1.0.0 requiremetns@1.0.0 requiremetnstxt@1.0.0 requiremetstx@1.0.0 requiremetstxt@1.0.0 requiremments@1.0.0 requiremmentstxt@1.0.0 requiremmentxt@1.0.0 requiremmentxtxt@1.0.0 requiremnets@1.0.0 requiremnetstxt@1.0.0 requiremnetxtxt@1.0.0 requiremnts@1.0.0 requiremntstx@1.0.0 requiremntstxt@1.0.0 requiremntxtxt@1.0.0 requiremtns@1.0.0 requirmeents@1.0.0 requirment@1.0.0 requirments@1.0.0 requirmentss@1.0.0 requirmentstx@1.0.0 requirmentstxt@1.0.0 requirmentstxtt@1.0.0 requirrementstxt@1.0.0 requirtements@1.0.0 requiurement@1.0.0 requiurementstxt@1.0.0 requksts@1.0.0 requnests@1.0.0 requrementstxt@1.0.0 requriements@1.0.0 requriments@1.0.0 requssts@1.0.0 requstss@1.0.0 requxsts@1.0.0 requyests@1.0.0 requzsts@1.0.0 reqzests@1.0.0 reuirements@1.0.0 seleenim@1.0.0 seleenimu@1.0.0 seleeniumm@1.0.0 seleinium@1.0.0 seleiniumm@1.0.0 seleinuim@1.0.0 seleiumm@1.0.0 selemiumm@1.0.0 selemni@1.0.0 selemnim@1.0.0 selemnium@1.0.0 selemniumm@1.0.0 selenimn@1.0.0 selennim@1.0.0 selenniumm@1.0.0 selennuim@1.0.0 selenuimm@1.0.0 selenyum@1.0.0 seleunium@1.0.0 seliniumm@1.0.0 seliniumn@1.0.0 selinum@1.0.0 selleium@1.0.0 selleniium@1.0.0 sellenim@1.0.0 selleniumm@1.0.0 sellinium@1.0.0 selunium@1.0.0 sijplejso@1.0.0 sijplejson@1.0.0 simepljson@1.0.0 simolejson@1.0.0 simpejso@1.0.0 simpjson@1.0.0 simpkejson@1.0.0 simplejason@1.0.0 simplejdon@1.0.0 simplejsoh@1.0.0 simplejsoj@1.0.0 simpoejson@1.0.0 siplejason@1.0.0 sjimplejson@1.0.0 sjmplejson@1.0.0 temsorflow@1.0.0 tensnflow@1.0.0 tensobflow@1.0.0 tensofklow@1.0.0 tensofl9w@1.0.0 tensofla@1.0.0 tensoflaow@1.0.0 tensofleow@1.0.0 tensofliw@1.0.0 tensofllow@1.0.0 tensofloaw@1.0.0 tensoflod@1.0.0 tensoflolw@1.0.0 tensoflom@1.0.0 tensoflomw@1.0.0 tensoflonw@1.0.0 tensoflor@1.0.0 tensoflouw@1.0.0 tensoflpw@1.0.0 tensoflqw@1.0.0 tensoflsw@1.0.0 tensoflw@1.0.0 tensoflxow@1.0.0 tensofpow@1.0.0 tensogflow@1.0.0 tensourflow@1.0.0 tensxoflow@1.0.0 trnsorflow@1.0.0