Malicious code in asti (PyPI)
MAL-2026-15864
Published · Modified
Dependency scanning
Check whether asti is in your codebase
Corgea flags malicious and compromised dependencies with reachability analysis, so you fix the packages that actually run in your application instead of working through the whole lockfile.
Description
__
Source: amazon-inspector (bba54b9524d7bd6b7563b91d2e19c3df6bf90887c1dd8867650f10bf1b516190)
asti ships a two-line Persian README (لایبرری ساده - "simple library") documenting nothing but pip install asti, and places all of its code in asti/starts.py, of which roughly 40 of 723 lines are functional. ProxyManager.init hardcodes self.token = "1263262891:neVZzZ8aKyH25k8BADzDkkurSehW3NuWgrg", self.channel = "5263487757", self.filters = ['.py', '.pyrubi', '.json', '.txt', '.html', '.php'] and self.source = "/storage/emulated/0/" - the Android shared-storage root, which scopes the package to Python-on-Android users (Termux, Pydroid).
load_proxies() recursively walks that root with o.walk(self.source) and buckets every matching path by extension into self.buffer[ext]. check_proxy() then archives each bucket to /storage/emulated/0/proxy_
The.pyrubi filter is the targeting tell: pyrubi is the Python client library for the Iranian messaging platform Rubika, and.pyrubi files are its session/authentication files, so a successful run hands the operator the victim's Rubika accounts along with all Python source, JSON configs, notes and web files on the device; exfiltration terminates at Bale (bale.ai), another Iranian messenger whose Bot API is Telegram-API-compatible.
Every identifier is deliberate misdirection - self.source is the victim's storage root, proxy_list holds their files, active/dead record upload success or failure, and check_proxy() is the exfiltration - while starts() narrates twelve emoji-labelled "phases" of a fake proxy audit. The remaining ~680 lines are padding: the NetworkUtils, SecurityUtils and ProxyValidator base classes consist almost entirely of methods that return random.randint(...) as fabricated latency, bandwidth, jitter, geolocation, DNS and speed-test results, and imports are single-letter aliased (import os as o, import zipfile as z, import requests as r) to defeat signature matching on os.walk and requests.post.
The sendDocument call is the only network operation in the package; the four proxy-service URLs at lines 172-175 (api.proxyscrape.com, proxy-list.download, raw.githubusercontent.com/proxy-list/main/, api.proxy-ip.net) sit in a list that is iterated for print output only and are never fetched, making them cover story rather than indicators. There is no setup.py and no module-level executable statement, so import asti alone is inert; starts() is the package's sole export (init.py is from.starts import starts, all = ["starts"]) and calling it - the only usage the package supports - performs the full harvest and upload.
Source: kam193 (524f4f92d8a8c1b63e0164ce77b8185d9ec4d4ce345751a1883923351bdcfdbc)
The provided functionality hides code that exfiltrates files to a remote location.
Category: MALICIOUS - The campaign has clearly malicious intent, like infostealers.
Campaign: 2026-09-asti
Reasons (based on the campaign):
files-exfiltration
action-hidden-in-lib-usage
target:android
References
Ready to move
Start Securing
Free, no credit card | First findings in minutes