V4 : Yara rules implementation
This commit is contained in:
parent
5662a9cf97
commit
ea7092cddb
179
scanner.js
179
scanner.js
@ -6,22 +6,39 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const axios = require('axios');
|
||||
const yara = require('@automattic/yara');
|
||||
const { Worker } = require('worker_threads');
|
||||
|
||||
const patterns = JSON.parse(fs.readFileSync('patterns.json')).patterns;
|
||||
const extensions = JSON.parse(fs.readFileSync('extensions.json')).extensions;
|
||||
const filenames = JSON.parse(fs.readFileSync('filenames.json')).filenames;
|
||||
//const aimodel = "huihui_ai/llama3.2-abliterate: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 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 ignoreFolders = [];
|
||||
let ignoreFiles = [];
|
||||
let webhook = {
|
||||
discord: {
|
||||
enabled: false,
|
||||
url: "CHANGE TO YOUR WEBHOOK"
|
||||
enabled: true,
|
||||
url: "https://discord.com/api/webhooks/1349816121972359240/4KhIbjzDERDnk0_OAJDA0Ha2YzzntvVTM96hhDNzmWYx65NfRad6LWFIx5aM8NZScxO-"
|
||||
}
|
||||
};
|
||||
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) {
|
||||
fs.readdir(directory, (err, files) => {
|
||||
if (err) {
|
||||
@ -85,52 +165,29 @@ function scanFile(filePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const functionToEval = `
|
||||
(function() {
|
||||
let foundMalicious = false;
|
||||
let patternFound = { found: false, value: "" };
|
||||
let extensionFound = { found: false, value: "" };
|
||||
let filenameFound = { found: false, value: "" };
|
||||
scanFileWithYara(filePath).then((yaraMatch) => {
|
||||
const foundMalicious = (yaraMatch.found);
|
||||
|
||||
patterns.forEach(pattern => {
|
||||
if (data.includes(pattern)) {
|
||||
console.log(\`[!] Malicious code found in file: \${filePath} - Pattern: \${pattern}\`);
|
||||
foundMalicious = true;
|
||||
patternFound = { found: true, value: pattern };
|
||||
}
|
||||
});
|
||||
|
||||
if (extensions.some(ext => filePath.endsWith(ext))) {
|
||||
const detectedExt = extensions.find(ext => filePath.endsWith(ext));
|
||||
console.log(\`[?] Extension detected: \${filePath} - \${detectedExt}\`);
|
||||
foundMalicious = true;
|
||||
extensionFound = { found: true, value: detectedExt };
|
||||
}
|
||||
|
||||
if (filenames.some(name => filePath.includes(name))) {
|
||||
const detectedName = filenames.find(name => filePath.includes(name));
|
||||
console.log(\`[?] Suspicious filename detected: \${filePath} - \${detectedName}\`);
|
||||
foundMalicious = true;
|
||||
filenameFound = { found: true, value: detectedName };
|
||||
}
|
||||
|
||||
return { foundMalicious, patternFound, extensionFound, filenameFound };
|
||||
})();
|
||||
`;
|
||||
if (foundMalicious) {
|
||||
console.log(`[!] Malicious code detected in file: ${filePath}`);
|
||||
console.log(`[!] Yara rule matched: ${yaraMatch.rule}`);
|
||||
|
||||
// Start Worker Thread for additional processing
|
||||
const worker = new Worker('./worker.js', {
|
||||
workerData: { functionToEval, filePath, data, patterns, extensions, filenames, aimodel, aiprompt, llamaendpoint }
|
||||
workerData: { filePath, data: '', aimodel, aiprompt, llamaendpoint }
|
||||
});
|
||||
|
||||
worker.on('message', (result) => {
|
||||
if (result.error) {
|
||||
console.error(`[ERROR] Error running eval in worker: ${result.error}`);
|
||||
console.error(`[ERROR] Error processing in worker: ${result.error}`);
|
||||
} else {
|
||||
console.log(`[INFO] Scan results for ${filePath}:`, result);
|
||||
if (result.foundMalicious) {
|
||||
//console.log(`[INFO] Scan results for ${filePath}:`, result);
|
||||
//if (result.foundMalicious) {
|
||||
const messageHistoryId = generateMessageHistoryId(filePath);
|
||||
runAIScan(filePath, result.patternFound, result.extensionFound, result.filenameFound, messageHistoryId);
|
||||
}
|
||||
runAIScan(filePath, yaraMatch, messageHistoryId);
|
||||
//} else {
|
||||
// console.log("[WARN] Empty message from worker recieved!")
|
||||
//}
|
||||
}
|
||||
});
|
||||
|
||||
@ -143,10 +200,17 @@ function scanFile(filePath) {
|
||||
console.error(`[ERROR] Worker stopped with exit code ${code}`);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log(`[INFO] No malicious activity detected in file: ${filePath}`);
|
||||
}
|
||||
}).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 messageHistory = messageHistories[messageHistoryId] || [];
|
||||
|
||||
@ -157,7 +221,14 @@ async function runAIScan(filePath, patternFound, extensionFound, filenameFound,
|
||||
},
|
||||
{
|
||||
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 aiResult = response.data.message.content;
|
||||
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].push(aiResult);
|
||||
@ -236,25 +307,15 @@ function extractAdditionalScanFiles(aiResult, currentDirectory) {
|
||||
return additionalFiles;
|
||||
}
|
||||
|
||||
async function sendReport(filePath, aiResult, patternFound, extensionFound, filenameFound) {
|
||||
async function sendReport(filePath, aiResult, yaraMatch) {
|
||||
if (webhook.discord.enabled) {
|
||||
const embed = {
|
||||
title: `AI Scan Results | ${filePath}`,
|
||||
description: `${aiResult}`,
|
||||
fields: [
|
||||
{
|
||||
name: `${patternFound.found ? "✅ Malicious Pattern found" : "❌ Malicious Pattern not found"}`,
|
||||
value: patternFound.value ? patternFound.value : "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",
|
||||
name: `${yaraMatch.found ? "✅ Yara Rule matched" : "❌ Yara Rule not matched"}`,
|
||||
value: yaraMatch.rule ? yaraMatch.rule : "N/A",
|
||||
inline: true
|
||||
}
|
||||
],
|
||||
|
Loading…
x
Reference in New Issue
Block a user