# ForceMemo - Chiến dịch Supply Chain Attack nhắm vào hàng trăm repo Python trên GitHub

## Tổng quan

Phát hiện chiến dịch tấn công nguy hiểm và tinh vi mang tên là **ForceMemo** , kẻ tấn công xâm chiếm hàng trăm tài khoản GitHub và tiêm mã độc giống hệt nhau vào hàng trăm repository Python. Các lần tiêm mã sớm nhất được ghi nhận từ ngày 8/3/2026, và chiến dịch vẫn còn đang hoạt động với các repo mới tiếp tục bị xâm phạm.

Cuộc tấn công nhắm vào các dự án Python bao gồm Django apps, ML research code, Streamlit dashboards, và PyPI packages bằng cách chèn mã bị obfuscate vào các file như [`setup.py`](http://setup.py), [`main.py`](http://main.py), và [`app.py`](http://app.py). Bất kỳ ai chạy `pip install` từ repo bị xâm phạm hoặc clone và thực thi code đó đều sẽ kích hoạt malware.

Dưới đây là bằng chứng điển hình: file [`setup.py`](http://setup.py) của repo `amirasaran/django-restful-admin` (70 stars) bị chèn mã độc obfuscated ở cuối file hợp lệ.

![](https://cdn.hashnode.com/uploads/covers/669e2c7992d73f3fd8335153/a382f486-7121-419f-82fc-1e3674e84c05.png align="center")

* * *

## Cơ chế tấn công 4 giai đoạn

### Giai đoạn 1: Chiếm tài khoản qua GlassWorm

Cơ chế chiếm tài khoản đã được xác định: các developer bị xâm phạm đã nhiễm malware **GlassWorm** thông qua các extension VS Code và Cursor độc hại. Payload stage 3 của GlassWorm bao gồm một module đánh cắp credential chuyên dụng, thu thập GitHub token từ nhiều nguồn: `git credential fill`, VS Code extension storage, `~/.git-credentials`, và biến môi trường `GITHUB_TOKEN`.

Bằng chứng cho thấy xâm phạm ở cấp độ tài khoản rất rõ ràng: khi một tài khoản có nhiều repository bị chiếm, **toàn bộ repo dưới tài khoản đó đều bị tiêm mã**. Ví dụ, user `BierOne` có 6 repo bị xâm phạm, tổ chức `wecode-bootcamp-korea` bị 6 repo, và `HydroRoll-Team` bị 6 repo.

Ảnh dưới đây minh họa GitHub code search cho thấy toàn bộ repo của tài khoản `BierOne` đều chứa malware giống hệt nhau bằng chứng điển hình của xâm phạm ở cấp tài khoản:

![](https://cdn.hashnode.com/uploads/covers/669e2c7992d73f3fd8335153/d66a8ddd-1784-4263-84f9-bf926fbc8550.png align="center")

* * *

### Giai đoạn 2: Tiêm mã lén lút qua Force-Push

Thay vì mở pull request hoặc tạo commit mới (cả hai đều hiển thị trong activity feed của repo), kẻ tấn công thực hiện 3 bước: lấy commit hợp lệ mới nhất trên default branch, rebase lại với malware được chèn vào một file Python chủ chốt, rồi force-push lên default branch. Message commit và author date đều được giữ nguyên từ commit gốc — chỉ có **committer date** mới lộ ra dấu hiệu giả mạo.

* * *

### Giai đoạn 3: C2 trên Solana Blockchain

Đây là điểm kỹ thuật đặc biệt nhất của chiến dịch. Thay vì kết nối tới một C2 server truyền thống có thể bị takedown, malware đọc lệnh từ **blockchain Solana**. Nó truy vấn một địa chỉ Solana cụ thể để lấy transaction memo chứa dữ liệu JSON với payload URL. Địa chỉ C2 là `BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC`.

Malware thử đến **9 Solana RPC endpoint khác nhau** làm fallback, khiến nó cực kỳ khó bị chặn khi chỉ block một endpoint đơn lẻ. Việc dùng blockchain làm kênh C2 có nghĩa là kẻ tấn công có thể cập nhật payload URL bất cứ lúc nào bằng cách đăng một transaction mới — và không ai có thể xóa hoặc kiểm duyệt các lệnh đó khi đã lên chain.

Một transaction ngày 25/2/2026 còn lộ ra cấu hình C2 trực tiếp — gồm C2 server trên port 5000, endpoint fingerprint IP nạn nhân, và DHT node trên port 10000 làm fallback peer-to-peer.

* * *

### Giai đoạn 4: Thực thi payload

Sau khi malware lấy được payload URL từ Solana memo, nó tải Node.js v22.9.0 từ [`nodejs.org`](http://nodejs.org) về home directory của người dùng (hỗ trợ đa nền tảng Windows/macOS/Linux, x64/ARM), tải payload JavaScript đã mã hóa, ghi file `i.js` để giải mã và thực thi payload qua Node.js đã tải, rồi tạo persistence file `~/init.json` với timer 2 ngày để tránh thực thi lặp lại.

* * *

## Tìm kiếm repo bị ảnh hưởng

Toàn bộ danh sách repo bị ảnh hưởng có thể tìm thấy bằng cách tìm kiếm biến marker của malware trên GitHub: `lzcdrtfxyqiplpd`.

[https://github.com/search?q=lzcdrtfxyqiplpd&type=code](https://github.com/search?q=lzcdrtfxyqiplpd&type=code)

![](https://cdn.hashnode.com/uploads/covers/669e2c7992d73f3fd8335153/20d5d3d4-38b0-418d-bd38-ac591e2771c0.png align="center")

## Cách kiểm tra nếu bị ảnh hưởng

Nếu bạn cài Python package trực tiếp từ GitHub hoặc clone và chạy repo Python, hãy kiểm tra bằng lệnh `grep -r "lzcdrtfxyqiplpd" .` để tìm marker variable, kiểm tra sự tồn tại của `~/init.json`, kiểm tra Node.js đã tải về với `ls ~/node-v22*`, kiểm tra file `i.js` trong các project directory, và xem lại git commit history tìm các commit có committer date mới hơn đáng kể so với author date.

* * *

## IOCs

<table style="min-width: 320px;"><colgroup><col style="min-width: 25px;"><col style="width: 295px;"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p><strong>Indicator</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p><strong>Value</strong></p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Solana C2 address</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Solana funding wallet</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>G2YxRa6wt1qePMwfJzdXZG62ej4qaTC7YURzuh2Lwd3t</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Marker variable</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>lzcdrtfxyqiplpd</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>XOR key</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>134</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Committer email</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>"null" (string)</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Node.js version</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>v22.9.0</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Persistence file</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>~/init.json</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>JS payload file</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>i.js</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Code comments lang</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>Russian</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>CIS exclusion</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>Bỏ qua thực thi nếu hệ thống dùng locale/timezone Nga</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>C2 server IPs</strong></p></td><td colspan="1" rowspan="1" colwidth="295"><p>45.32.151.157, 45.32.150.97, 217.69.11.57, 217.69.11.99, 217.69.0.159, 45.76.44.240</p></td></tr></tbody></table>

## Tham khảo

[GlassWorm Attack Uses Stolen GitHub Tokens to Force-Push Malware Into Python Repos](https://thehackernews.com/2026/03/glassworm-attack-uses-stolen-github.html)

[ForceMemo: Hundreds of GitHub Python Repos Compromised via Account Takeover and Force-Push](https://www.stepsecurity.io/blog/forcememo-hundreds-of-github-python-repos-compromised-via-account-takeover-and-force-push)
