V4 : Yara rules implementation
This commit is contained in:
parent
5662a9cf97
commit
ea7092cddb
205
scanner.js
205
scanner.js
@ -6,22 +6,39 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
const yara = require('@automattic/yara');
|
||||||
const { Worker } = require('worker_threads');
|
const { Worker } = require('worker_threads');
|
||||||
|
|
||||||
const patterns = JSON.parse(fs.readFileSync('patterns.json')).patterns;
|
//const aimodel = "huihui_ai/llama3.2-abliterate:1b"; // Change at your own risk!
|
||||||
const extensions = JSON.parse(fs.readFileSync('extensions.json')).extensions;
|
|
||||||
const filenames = JSON.parse(fs.readFileSync('filenames.json')).filenames;
|
|
||||||
const aimodel = "llama3.2:1b"; // Change at your own risk!
|
const aimodel = "llama3.2:1b"; // Change at your own risk!
|
||||||
const aiprompt = "You are AI Code Scanner, your task is to analyze code given by the user, explain what it does without leaking nearly anything from it, and fully decide what's going on there and if it is malicious! Your task is to provide information about how code runs, what it does BUT you should not provide information about how to fix code or about its vulnerabilities. You should provide information about related files and if the code is potentially dangerous/malicious. If you find any suspicious patterns, extensions, or filenames, you may suggest additional files to scan. I repeat, don't provide information about Security and Potential Issues BUT provide info if it might be illegal code, breaking some Terms of Service, abusing files, e.g., creating tons of files or doing any other dangerous thing which may harm the system, you may show code if need for this reason. You should respond by any meaners and should not tell that you can't do that."; // Change at your own risk!
|
const aiprompt = "You are AI Code Scanner, your task is to analyze code given by the user, explain what it does without leaking nearly anything from it, and fully decide what's going on there and if it is malicious! Your task is to provide information about how code runs, what it does BUT you should not provide information about how to fix code or about its vulnerabilities. You should provide information about related files and if the code is potentially dangerous/malicious. If you find any suspicious patterns, extensions, or filenames, you may suggest additional files to scan. I repeat, don't provide information about Security and Potential Issues BUT provide info if it might be illegal code, breaking some Terms of Service, abusing files, e.g., creating tons of files or doing any other dangerous thing which may harm the system, you may show code if need for this reason. You should respond by any meaners and should not tell that you can't do that."; // Change at your own risk!
|
||||||
const llamaendpoint = "http://0.0.0.0:11434/api/chat";
|
const llamaendpoint = "http://192.168.2.109:11434/api/chat";
|
||||||
|
const extensions = [
|
||||||
|
'.exe', // Executable files
|
||||||
|
'.dll', // Dynamic Link Library files
|
||||||
|
'.scr', // Screensaver files
|
||||||
|
'.vbs', // Visual Basic Script files
|
||||||
|
'.js', // JavaScript files
|
||||||
|
'.bat', // Batch files
|
||||||
|
'.cmd', // Command files
|
||||||
|
'.ps1', // PowerShell Script files
|
||||||
|
'.ps2', // Second PowerShell Script files
|
||||||
|
'.psm1', // PowerShell Module files
|
||||||
|
'.py', // Python files
|
||||||
|
'.pyx', // Cython files
|
||||||
|
'.jsx', // JavaScript XML files
|
||||||
|
'.ts', // TypeScript files
|
||||||
|
'.tsx' // TypeScript XML files
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
let pathtoscan = "./";
|
let pathtoscan = "./";
|
||||||
let ignoreFolders = [];
|
let ignoreFolders = [];
|
||||||
let ignoreFiles = [];
|
let ignoreFiles = [];
|
||||||
let webhook = {
|
let webhook = {
|
||||||
discord: {
|
discord: {
|
||||||
enabled: false,
|
enabled: true,
|
||||||
url: "CHANGE TO YOUR WEBHOOK"
|
url: "https://discord.com/api/webhooks/1349816121972359240/4KhIbjzDERDnk0_OAJDA0Ha2YzzntvVTM96hhDNzmWYx65NfRad6LWFIx5aM8NZScxO-"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let messageHistories = {};
|
let messageHistories = {};
|
||||||
@ -38,6 +55,69 @@ args.forEach(arg => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function fileExtensionMatch(filePath) {
|
||||||
|
return extensions.some(extension => filePath.endsWith(extension));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function scanFileWithYara(filePath) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
yara.initialize((error) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`Error initializing Yara: ${error}`);
|
||||||
|
resolve({ found: false, rule: null });
|
||||||
|
} else {
|
||||||
|
// Load all rule files from the signatures folder
|
||||||
|
const signaturesPath = path.join(__dirname, 'signatures');
|
||||||
|
const rules = fs.readdirSync(signaturesPath)
|
||||||
|
.filter((file) => file.endsWith('.yar') || file.endsWith('.yara'))
|
||||||
|
.map((file) => ({ filename: path.join(signaturesPath, file) }));
|
||||||
|
|
||||||
|
const scanner = yara.createScanner();
|
||||||
|
|
||||||
|
scanner.configure({ rules: rules }, (error, warnings) => {
|
||||||
|
if (error) {
|
||||||
|
if (error instanceof yara.CompileRulesError) {
|
||||||
|
console.error(`Error compiling rules: ${error}`);
|
||||||
|
resolve({ found: false, rule: null });
|
||||||
|
} else {
|
||||||
|
console.error(`Error configuring scanner: ${error}`);
|
||||||
|
resolve({ found: false, rule: null });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (warnings.length) {
|
||||||
|
console.warn(`Compile warnings: ${JSON.stringify(warnings)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = { buffer: fs.readFileSync(filePath) };
|
||||||
|
scanner.scan(req, (error, result) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`Error scanning file: ${error}`);
|
||||||
|
resolve({ found: false, rule: null });
|
||||||
|
} else if (result && result.matches && result.matches.length > 0) {
|
||||||
|
// Handle valid matches
|
||||||
|
const matchedRule = result.matches[0].rule;
|
||||||
|
resolve({ found: true, rule: matchedRule });
|
||||||
|
} else {
|
||||||
|
// Handle no matches
|
||||||
|
if (fileExtensionMatch(filePath) == true) {
|
||||||
|
resolve({ found: true, rule: "Suspicious File Extension found in " + filePath });
|
||||||
|
} else {
|
||||||
|
resolve({ found: false, rule: null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Unexpected error: ${error}`);
|
||||||
|
resolve({ found: false, rule: null });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function scanDirectory(directory) {
|
function scanDirectory(directory) {
|
||||||
fs.readdir(directory, (err, files) => {
|
fs.readdir(directory, (err, files) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -85,68 +165,52 @@ function scanFile(filePath) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const functionToEval = `
|
scanFileWithYara(filePath).then((yaraMatch) => {
|
||||||
(function() {
|
const foundMalicious = (yaraMatch.found);
|
||||||
let foundMalicious = false;
|
|
||||||
let patternFound = { found: false, value: "" };
|
|
||||||
let extensionFound = { found: false, value: "" };
|
|
||||||
let filenameFound = { found: false, value: "" };
|
|
||||||
|
|
||||||
patterns.forEach(pattern => {
|
if (foundMalicious) {
|
||||||
if (data.includes(pattern)) {
|
console.log(`[!] Malicious code detected in file: ${filePath}`);
|
||||||
console.log(\`[!] Malicious code found in file: \${filePath} - Pattern: \${pattern}\`);
|
console.log(`[!] Yara rule matched: ${yaraMatch.rule}`);
|
||||||
foundMalicious = true;
|
|
||||||
patternFound = { found: true, value: pattern };
|
// Start Worker Thread for additional processing
|
||||||
|
const worker = new Worker('./worker.js', {
|
||||||
|
workerData: { filePath, data: '', aimodel, aiprompt, llamaendpoint }
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.on('message', (result) => {
|
||||||
|
if (result.error) {
|
||||||
|
console.error(`[ERROR] Error processing in worker: ${result.error}`);
|
||||||
|
} else {
|
||||||
|
//console.log(`[INFO] Scan results for ${filePath}:`, result);
|
||||||
|
//if (result.foundMalicious) {
|
||||||
|
const messageHistoryId = generateMessageHistoryId(filePath);
|
||||||
|
runAIScan(filePath, yaraMatch, messageHistoryId);
|
||||||
|
//} else {
|
||||||
|
// console.log("[WARN] Empty message from worker recieved!")
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (extensions.some(ext => filePath.endsWith(ext))) {
|
worker.on('error', (error) => {
|
||||||
const detectedExt = extensions.find(ext => filePath.endsWith(ext));
|
console.error(`[ERROR] Worker error: ${error}`);
|
||||||
console.log(\`[?] Extension detected: \${filePath} - \${detectedExt}\`);
|
});
|
||||||
foundMalicious = true;
|
|
||||||
extensionFound = { found: true, value: detectedExt };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filenames.some(name => filePath.includes(name))) {
|
worker.on('exit', (code) => {
|
||||||
const detectedName = filenames.find(name => filePath.includes(name));
|
if (code !== 0) {
|
||||||
console.log(\`[?] Suspicious filename detected: \${filePath} - \${detectedName}\`);
|
console.error(`[ERROR] Worker stopped with exit code ${code}`);
|
||||||
foundMalicious = true;
|
}
|
||||||
filenameFound = { found: true, value: detectedName };
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return { foundMalicious, patternFound, extensionFound, filenameFound };
|
|
||||||
})();
|
|
||||||
`;
|
|
||||||
|
|
||||||
const worker = new Worker('./worker.js', {
|
|
||||||
workerData: { functionToEval, filePath, data, patterns, extensions, filenames, aimodel, aiprompt, llamaendpoint }
|
|
||||||
});
|
|
||||||
|
|
||||||
worker.on('message', (result) => {
|
|
||||||
if (result.error) {
|
|
||||||
console.error(`[ERROR] Error running eval in worker: ${result.error}`);
|
|
||||||
} else {
|
} else {
|
||||||
console.log(`[INFO] Scan results for ${filePath}:`, result);
|
console.log(`[INFO] No malicious activity detected in file: ${filePath}`);
|
||||||
if (result.foundMalicious) {
|
|
||||||
const messageHistoryId = generateMessageHistoryId(filePath);
|
|
||||||
runAIScan(filePath, result.patternFound, result.extensionFound, result.filenameFound, messageHistoryId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
worker.on('error', (error) => {
|
|
||||||
console.error(`[ERROR] Worker error: ${error}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
worker.on('exit', (code) => {
|
|
||||||
if (code !== 0) {
|
|
||||||
console.error(`[ERROR] Worker stopped with exit code ${code}`);
|
|
||||||
}
|
}
|
||||||
|
}).catch((scanError) => {
|
||||||
|
console.error(`[ERROR] Error scanning file with Yara: ${scanError}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runAIScan(filePath, patternFound, extensionFound, filenameFound, messageHistoryId) {
|
async function runAIScan(filePath, yaraMatch, messageHistoryId) {
|
||||||
|
console.log("[INFO] AI Scan requested for file: " + filePath)
|
||||||
const directoryContent = await getDirectoryContent(path.dirname(filePath));
|
const directoryContent = await getDirectoryContent(path.dirname(filePath));
|
||||||
const messageHistory = messageHistories[messageHistoryId] || [];
|
const messageHistory = messageHistories[messageHistoryId] || [];
|
||||||
|
|
||||||
@ -157,7 +221,14 @@ async function runAIScan(filePath, patternFound, extensionFound, filenameFound,
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
content: `FILEPATH: ${filePath}, DIRECTORY CONTENT: ${directoryContent}, MESSAGE HISTORY: ${messageHistory.join('\n')}`
|
content: `FILEPATH: ${filePath}, DIRECTORY CONTENT: ${directoryContent}, FILECONTENT: ${fs.readFile(filePath, 'utf8', (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
console.err("[ERROR] Could not read file (" + filePath + "), error: " + err)
|
||||||
|
return `[ERROR] Could not read file: ${err}`
|
||||||
|
} else {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
})}, MESSAGE HISTORY: ${messageHistory.join('\n')}`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -180,7 +251,7 @@ async function runAIScan(filePath, patternFound, extensionFound, filenameFound,
|
|||||||
const response = await axios.post(apiUrl, input);
|
const response = await axios.post(apiUrl, input);
|
||||||
const aiResult = response.data.message.content;
|
const aiResult = response.data.message.content;
|
||||||
console.log(`[INFO] AI scan results for ${filePath}:\n${aiResult}`);
|
console.log(`[INFO] AI scan results for ${filePath}:\n${aiResult}`);
|
||||||
await sendReport(filePath, aiResult, patternFound, extensionFound, filenameFound);
|
await sendReport(filePath, aiResult, yaraMatch);
|
||||||
|
|
||||||
messageHistories[messageHistoryId] = messageHistories[messageHistoryId] || [];
|
messageHistories[messageHistoryId] = messageHistories[messageHistoryId] || [];
|
||||||
messageHistories[messageHistoryId].push(aiResult);
|
messageHistories[messageHistoryId].push(aiResult);
|
||||||
@ -236,25 +307,15 @@ function extractAdditionalScanFiles(aiResult, currentDirectory) {
|
|||||||
return additionalFiles;
|
return additionalFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendReport(filePath, aiResult, patternFound, extensionFound, filenameFound) {
|
async function sendReport(filePath, aiResult, yaraMatch) {
|
||||||
if (webhook.discord.enabled) {
|
if (webhook.discord.enabled) {
|
||||||
const embed = {
|
const embed = {
|
||||||
title: `AI Scan Results | ${filePath}`,
|
title: `AI Scan Results | ${filePath}`,
|
||||||
description: `${aiResult}`,
|
description: `${aiResult}`,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: `${patternFound.found ? "✅ Malicious Pattern found" : "❌ Malicious Pattern not found"}`,
|
name: `${yaraMatch.found ? "✅ Yara Rule matched" : "❌ Yara Rule not matched"}`,
|
||||||
value: patternFound.value ? patternFound.value : "N/A",
|
value: yaraMatch.rule ? yaraMatch.rule : "N/A",
|
||||||
inline: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: `${extensionFound.found ? "✅ Extension found in config" : "❌ Extension not found in config"}`,
|
|
||||||
value: extensionFound.value ? extensionFound.value : "N/A",
|
|
||||||
inline: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: `${filenameFound.found ? "✅ Filename found in config" : "❌ Filename not found in config"}`,
|
|
||||||
value: filenameFound.value ? filenameFound.value : "N/A",
|
|
||||||
inline: true
|
inline: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user