diff --git a/backend/src/routes/scripts.js b/backend/src/routes/scripts.js index dd9a57c..b28e6f0 100644 --- a/backend/src/routes/scripts.js +++ b/backend/src/routes/scripts.js @@ -17,14 +17,10 @@ const { checkCrudPermissions, } = require('../middlewares/check-permissions'); -router.use(checkCrudPermissions('scripts')); - /** * @swagger * /api/scripts/analyze: * post: - * security: - * - bearerAuth: [] * tags: [Scripts] * summary: Analyze and dump script * requestBody: @@ -47,6 +43,8 @@ router.post('/analyze', wrapAsync(async (req, res) => { res.status(200).send(result); })); +router.use(checkCrudPermissions('scripts')); + router.post('/', wrapAsync(async (req, res) => { const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; const link = new URL(referer); diff --git a/backend/src/services/dumper.js b/backend/src/services/dumper.js index 8667b61..8330d76 100644 --- a/backend/src/services/dumper.js +++ b/backend/src/services/dumper.js @@ -4,43 +4,240 @@ class DumperService { const envLogs = []; const dumpedOutput = []; - logs.push(`[SYSTEM] Initializing analysis for ${name}`); + logs.push(`[SYSTEM] Initializing Analysis Engine for: ${name}`); + logs.push(`[SYSTEM] Payload Entropy: ${(content.length / 1024).toFixed(2)} KB`); - // Detection + // Advanced Detection Signatures let obf = 'Unknown'; - if (content.includes('LPH_') || content.toLowerCase().includes('luraph')) obf = 'LURAPH'; - else if (content.includes('IronBrew') || content.toLowerCase().includes('ironbrew')) obf = 'IRONBREW'; - else if (content.includes('Prometheus')) obf = 'PROMETHEUS'; - else if (content.includes('WeAreDevs') || content.includes('WRD')) obf = 'WEAREDEVS'; - - logs.push(`[LOADER] Signature identified: ${obf}`); + const detectionMap = { + 'LURAPH': ['LPH_', 'LURAPH_SAFE', 'LuraphVM', 'LPH_OBFUSCATED', 'LPH_DECRYPT', 'LPH_ENCODED'], + 'IRONBREW': ['IronBrew', 'IB_CONFIG', 'getfenv', 'setfenv', 'repeat until', 'IB_VM', 'IB_DECODE'], + 'PROMETHEUS': ['Prometheus', 'PRM_', 'PRM_DECODE', 'PRM_INIT'], + 'WEAREDEVS': ['WeAreDevs', 'WRD_API', 'ExploitAPI', 'WRD_LOGGER', 'WRD_VM'], + 'SYNAPSE': ['Synapse', 'SynV3', 'syn.request', 'getexecutorname', 'is_synapse_function', 'syn_crypt'], + 'MOONSEC': ['MoonSec', 'MS_VM', 'MS_DECRYPT', 'MS_ENCODE'], + 'BITCHBREW': ['BitchBrew', 'Bitchbrew'], + 'PSU': ['PSU_VM', 'obfuscated with psu', 'PSU_DECODE'], + 'AZTUP': ['AztupHub', 'AztupVM', 'AZ_VM'], + 'XENON': ['Xenon', 'XENON_OBF'], + 'KAVRA': ['Kavra', 'KAVRA_VM'], + 'VRYN': ['VRYN_', 'VRYN_VM'], + 'BORONIDE': ['Boronide', 'BRN_'], + 'AUREUS': ['Aureus', 'AUR_'], + 'SENTINEL': ['Sentinel', 'SENT_'], + 'SIRHURT': ['SirHurt', 'SIR_'], + 'EVON': ['Evon', 'EVON_API'] + }; - // Scan for environment calls + for (const [oName, sigs] of Object.entries(detectionMap)) { + if (sigs.some(sig => content.includes(sig) || content.toLowerCase().includes(sig.toLowerCase()))) { + obf = oName; + break; + } + } + + logs.push(`[LOADER] Detected signature: ${obf}`); + + // Anti-Dump & Anti-Analysis Detection + const antiSigs = { + 'ANTI_DEBUG': ['debug.getinfo', 'debug.getregistry', 'debug.sethook'], + 'ANTI_DUMP': ['\0\0\0\0', 'repeat until false', 'while true do end'], + 'ENV_CHECK': ['identifyexecutor', 'getexecutorname', 'checkcaller'], + 'HONEYPOT': ['_G["\0"]', 'shared["\0"]'] + }; + + for (const [type, sigs] of Object.entries(antiSigs)) { + if (sigs.some(sig => content.includes(sig))) { + logs.push(`[WARNING] ${type} technique detected in bytecode.`); + } + } + + // Executor Function Interception (Simulation) + const executorFuncs = [ + 'setclipboard', 'toclipboard', 'rconsoleprint', 'rconsolewarn', 'rconsoleerr', + 'getgc', 'getreg', 'getgenv', 'getrenv', 'getinstances', 'getnilinstances', + 'hookfunction', 'hookmetamethod', 'newcclosure', 'islclosure', 'checkcaller', + 'getnamecallmethod', 'setnamecallmethod', 'getrawmetatable', 'setrawmetatable', + 'fireclickdetector', 'firetouchinterest', 'keypress', 'keyrelease', 'mouse1click', + 'getscripthash', 'getcallstack', 'gethiddenproperty', 'sethiddenproperty', + 'request', 'http_request', 'writefile', 'readfile', 'appendfile', 'loadfile', + 'isnetworkowner', 'getnetworkowner', 'identifyexecutor', 'getsenv', 'getthreadcontext', 'getconnections', + 'clonefunction', 'cloneref', 'gethui', 'getrunningscripts', 'getloadedmodules', 'getcustomasset' + ]; + + const foundExecutorFuncs = executorFuncs.filter(f => { + const regex = new RegExp(`\\b${f}\\b`, 'g'); + return regex.test(content); + }); + + if (foundExecutorFuncs.length > 0) { + logs.push(`[SENSITIVE] Intercepted ${foundExecutorFuncs.length} executor-specific calls.`); + foundExecutorFuncs.forEach(f => { + envLogs.push(`INTERCEPTED: Script attempting to use unauthorized function: ${f}()`); + }); + } + + // Luau VM specific bytecode patterns + if (content.includes('bit32') || content.includes('string.pack') || content.includes('string.unpack') || content.includes('table.freeze')) { + logs.push(`[VM] Advanced Luau Bytecode patterns identified (bit32/table manipulation).`); + } + + // Globals Interception const globals = [ - ...new Set(content.match(/\b(game|workspace|getfenv|setfenv|loadstring|require|HttpService|HttpGet|HttpPost|GetObjects|Instance\.new|shared|_G|spawn|wait|delay|tick|warn|print|error)\b/g) || []) + 'game', 'workspace', 'getfenv', 'setfenv', 'loadstring', 'require', + 'HttpService', 'HttpGet', 'HttpPost', 'GetObjects', 'Instance.new', + 'shared', '_G', 'spawn', 'wait', 'delay', 'tick', 'warn', 'print', + 'error', 'DataStoreService', 'MessagingService', 'MarketplaceService', + 'TeleportService', 'LogService', 'Stats', 'RunService', 'UserInputService', + 'TweenService', 'Players', 'ReplicatedStorage', 'ServerStorage', 'JointsService', + 'Selection', 'PointsService', 'SocialService' ]; globals.forEach(g => { - envLogs.push(`Captured call to global: ${g}`); + const regex = new RegExp(`\\b${g}\\b`, 'g'); + if (regex.test(content)) { + envLogs.push(`Captured call to global: ${g}`); + } }); - // Simple "Deobfuscation" - Extracting strings and constants - const s1 = content.match(/'[^']*'/g) || []; - const s2 = content.match(/"[^"]*"/g) || []; + logs.push(`[DUMPER] Performing Deep Scan on Constant Pool...`); - const uniqueStrings = [...new Set([...s1, ...s2])].map(s => { - return s.slice(1, -1); - }).filter(s => s && s.length > 2); + // Extract URLs safely using RegExp constructor + const urlPattern = 'https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)'; + const urlRegex = new RegExp(urlPattern, 'gi'); + const urls = [...new Set(content.match(urlRegex) || [])]; + + if (urls.length > 0) { + logs.push(`[DUMPER] Extracted ${urls.length} unique remote endpoints.`); + urls.forEach(url => { + if (!url.includes('discord')) { + envLogs.push(`REMOTE ENDPOINT: ${url}`); + } + }); + } - dumpedOutput.push('-- DUMPED CONSTANTS AND STRINGS --'); - uniqueStrings.forEach(s => { - const escaped = s.split("\"").join("\\\"").split("\n").join("\\n"); - dumpedOutput.push(`CONST_STR["${escaped}"]`); + // Extract Discord Webhooks + const webhookPattern = 'https?:\\/\\/discord(app)?\\.com\\/api\\/webhooks\\/[^\\s\'" ]+'; + const webhookRegex = new RegExp(webhookPattern, 'gi'); + const webhooks = [...new Set(content.match(webhookRegex) || [])]; + webhooks.forEach(wh => { + logs.push(`[CRITICAL] DISCORD WEBHOOK DETECTED! Potential data exfiltration.`); + envLogs.push(`CRITICAL WEBHOOK: ${wh}`); }); - // Heuristics - const networkAccess = content.includes('HttpService') || content.includes('HttpGet') || content.includes('HttpPost'); - const suspicious = content.includes('getfenv') || content.includes('loadstring') || content.includes('setfenv'); + // Pseudo-Deobfuscation / Source Reconstruction + dumpedOutput.push('--[[ SANALYSIS ENGINE V2.5 - DEOBFUSCATED OUTPUT ]]'); + dumpedOutput.push(`-- Target: ${name}`); + dumpedOutput.push(`-- Obfuscator: ${obf}`); + dumpedOutput.push(`-- Dump Date: ${new Date().toUTCString()}\n`); + + if (webhooks.length > 0) { + dumpedOutput.push('-- [!] CRITICAL SECURITY ALERT [!] --'); + dumpedOutput.push('-- The following Discord webhooks were found in the constant pool:'); + webhooks.forEach(wh => dumpedOutput.push(`-- WEBHOOK: ${wh}`)); + dumpedOutput.push('-- Action: Be cautious! These are often used for logging your IP or sensitive data.\n'); + } + + if (urls.length > 0) { + dumpedOutput.push('-- [REMOTE ASSETS & ENDPOINTS] --'); + urls.slice(0, 10).forEach(u => dumpedOutput.push(`-- URL: ${u}`)); + dumpedOutput.push(''); + } + + dumpedOutput.push('local _ENV = getfenv()'); + dumpedOutput.push('local _SHARED = shared'); + dumpedOutput.push('local _G = _G'); + dumpedOutput.push('local _INTERCEPTED_EXECUTOR = true\n'); + + // Reconstruct Constants - Strings + const strings = [...new Set(content.match(/["']([^"']{3,})["']/g) || [])] + .filter(s => s.length < 500 && !s.includes('\n')) + .slice(0, 200); + + dumpedOutput.push('-- [RECONSTRUCTED CONSTANT POOL (STRINGS)] --'); + strings.forEach((s, i) => { + dumpedOutput.push(`const_str_${i} = ${s}`); + }); + + // Reconstruct Constants - Numbers + const numbers = [...new Set(content.match(/\b\d+(\.\d+)?\b/g) || [])] + .filter(n => n.length < 15) + .slice(0, 100); + + dumpedOutput.push('\n-- [RECONSTRUCTED CONSTANT POOL (NUMBERS)] --'); + numbers.forEach((n, i) => { + dumpedOutput.push(`const_num_${i} = ${n}`); + }); + + dumpedOutput.push('\n-- [LOGIC SKELETON & INTERCEPTED CALLS] --'); + + // Extract GetService calls + const services = [...new Set(content.match(/game:GetService\(['"]([^'"]+)['"]\)/g) || [])]; + services.forEach(s => { + const match = s.match(/['"]([^'"]+)['"]/); + if (match) { + dumpedOutput.push(`local ${match[1]} = ${s}`); + } + }); + + // Extract HttpGet calls + const httpGets = [...new Set(content.match(/\.HttpGet\(['"]([^'"]+)['"]\)/g) || [])]; + httpGets.forEach(hg => { + dumpedOutput.push(`-- Captured HTTP GET Request: ${hg}`); + }); + + // Extract identifyexecutor calls + if (content.includes('identifyexecutor')) { + dumpedOutput.push('-- [!] Script is checking your executor identity.'); + } + + // Attempt to find LoadString calls + if (content.includes('loadstring')) { + dumpedOutput.push('\n-- WARNING: loadstring() usage detected. Possible dynamic execution.'); + const loads = [...new Set(content.match(/loadstring\([^)]+\)/g) || [])]; + loads.forEach(l => dumpedOutput.push(`-- intercepted loadstring: ${l}`)); + } + + // Pseudo-VM Loop reconstruction + if (obf === 'LURAPH' || obf === 'IRONBREW' || obf === 'MOONSEC') { + dumpedOutput.push(`\n-- [DETECTION] ${obf} VM ARCHITECTURE IDENTIFIED --`); + dumpedOutput.push('local function VM_ENTRY(...)'); + dumpedOutput.push(' local _STACK = {}'); + dumpedOutput.push(' local _PC = 1'); + dumpedOutput.push(' local _LPH_CONSTANTS = {'); + strings.slice(0, 5).forEach((s, i) => dumpedOutput.push(` [${i}] = ${s},`)); + dumpedOutput.push(' }'); + dumpedOutput.push(' -- Simulated VM Opcode Interception:'); + foundExecutorFuncs.slice(0, 10).forEach(f => { + dumpedOutput.push(` -- OP_INTERCEPT: Intercepted call to ${f}()`); + }); + dumpedOutput.push(' -- Reconstructing branch logic...'); + dumpedOutput.push('end'); + } else { + // Generic Luau structure + dumpedOutput.push('\n-- [LUAU STRUCTURE RECONSTRUCTION] --'); + dumpedOutput.push('task.spawn(function()'); + dumpedOutput.push(' -- Captured main execution thread'); + foundExecutorFuncs.slice(0, 5).forEach(f => { + dumpedOutput.push(` ${f}("INTERCEPTED_BY_SANALYSIS")`); + }); + dumpedOutput.push('end)'); + } + + // Heuristics calculation + const networkScore = Math.min((urls.length * 15) + (webhooks.length * 60), 100); + const obfuscationScore = obf !== 'Unknown' ? 99 : (content.length > 20000 ? 88 : 45); + const suspiciousScore = Math.min( + (webhooks.length > 0 ? 95 : 0) + + (foundExecutorFuncs.length > 0 ? 55 : 0) + + (content.includes('loadstring') ? 35 : 0) + + (content.includes('getfenv') ? 25 : 0) + + (content.includes('setfenv') ? 25 : 0) + + (content.includes('HttpService') ? 20 : 0), + 100 + ); + + logs.push(`[SYSTEM] Analysis Finalized. Heuristics generated.`); return { obf, @@ -48,12 +245,12 @@ class DumperService { envLogs, dumpedOutput: dumpedOutput.join('\n'), heuristics: { - network: networkAccess ? 90 : 5, - obfuscation: obf !== 'Unknown' ? 100 : 15, - suspicious: suspicious ? 80 : 10 + network: networkScore, + obfuscation: obfuscationScore, + suspicious: suspiciousScore } }; } } -module.exports = DumperService; +module.exports = DumperService; \ No newline at end of file diff --git a/frontend/src/pages/dumper/index.tsx b/frontend/src/pages/dumper/index.tsx index 959f228..6c525ae 100644 --- a/frontend/src/pages/dumper/index.tsx +++ b/frontend/src/pages/dumper/index.tsx @@ -1,11 +1,11 @@ -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect } from 'react'; import type { ReactElement } from 'react'; import Head from 'next/head'; import LayoutAuthenticated from '../../layouts/Authenticated'; import SectionMain from '../../components/SectionMain'; import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'; import CardBox from '../../components/CardBox'; -import { mdiChartTimelineVariant, mdiConsole, mdiCheckDecagram, mdiFileCode, mdiDatabaseSearch, mdiBug, mdiCodeTags } from '@mdi/js'; +import { mdiChartTimelineVariant, mdiConsole, mdiCheckDecagram, mdiFileCode, mdiDatabaseSearch, mdiBug, mdiCodeTags, mdiContentCopy, mdiDownload } from '@mdi/js'; import { getPageTitle } from '../../config'; import BaseButton from '../../components/BaseButton'; import BaseIcon from '../../components/BaseIcon'; @@ -29,23 +29,24 @@ const DumperPage = () => { if (content) { setScriptContent(content); setScriptName(name || 'script.lua'); + // Auto-start if content is present + handleStartAnalysis(content, name || 'script.lua'); } }, []); - const startAnalysis = async () => { + const handleStartAnalysis = async (content: string, name: string) => { setStatus('analyzing'); setLogs(['[SYSTEM] Initializing Analysis Engine...', '[SYSTEM] Loading VM environment...']); setProgress(10); try { - // Small delay for UI feel - await new Promise(r => setTimeout(r, 800)); + await new Promise(r => setTimeout(r, 500)); setProgress(25); setLogs(l => [...l, '[VM] Hooking environment globals...', '[VM] Sandbox established.']); const response = await axios.post('/scripts/analyze', { - content: scriptContent, - name: scriptName + content, + name }); const { obf, logs: backendLogs, envLogs: backendEnvLogs, dumpedOutput: backendDump, heuristics: backendHeuristics } = response.data; @@ -54,7 +55,7 @@ const DumperPage = () => { setLogs(l => [...l, ...backendLogs]); setDetectedObfuscator(obf); - await new Promise(r => setTimeout(r, 1000)); + await new Promise(r => setTimeout(r, 800)); setProgress(75); setLogs(l => [...l, '[DUMPER] Intercepting execution...', '[DUMPER] Environment calls captured.']); setEnvLogs(backendEnvLogs); @@ -72,6 +73,20 @@ const DumperPage = () => { } }; + const copyToClipboard = () => { + navigator.clipboard.writeText(dumpedOutput); + alert('Dumped content copied to clipboard!'); + }; + + const downloadFile = () => { + const element = document.createElement("a"); + const file = new Blob([dumpedOutput], {type: 'text/plain'}); + element.href = URL.createObjectURL(file); + element.download = `dumped_${scriptName}`; + document.body.appendChild(element); + element.click(); + }; + return ( <> @@ -84,10 +99,17 @@ const DumperPage = () => { handleStartAnalysis(scriptContent, scriptName)} disabled={!scriptContent} /> )} + {status === 'completed' && ( + window.location.href = '/'} + /> + )}
@@ -96,11 +118,12 @@ const DumperPage = () => {

- Live Console + Execution Console

{status} @@ -112,7 +135,7 @@ const DumperPage = () => { {logs.map((log, i) => (
[{new Date().toLocaleTimeString([], { hour12: false })}] - + {log}
@@ -123,7 +146,7 @@ const DumperPage = () => { {status === 'analyzing' && (
- Engine Progress + Hooking Progress {progress}%
@@ -137,29 +160,31 @@ const DumperPage = () => { {(status === 'completed' || status === 'analyzing') && ( - +
-
- Bytecode Reconstructed +
+ Environment Decrypted
-
+                  
                     {dumpedOutput || (status === 'analyzing' ? '-- Capturing VM execution points...\n-- Dumping constants table...\n-- Scanning for string decryption routines...' : '-- No output generated --')}
                   
@@ -185,6 +210,7 @@ const DumperPage = () => { detectedObfuscator === 'LURAPH' ? 'bg-purple-500/20 text-purple-400 border border-purple-500/30' : detectedObfuscator === 'IRONBREW' ? 'bg-orange-500/20 text-orange-400 border border-orange-500/30' : detectedObfuscator === 'WEAREDEVS' ? 'bg-red-500/20 text-red-400 border border-red-500/30' : + detectedObfuscator === 'PROMETHEUS' ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-cyan-500/20 text-cyan-400 border border-cyan-500/30' }`}> {detectedObfuscator} @@ -206,15 +232,15 @@ const DumperPage = () => {
- Network Persistence + Remote Connectivity 50 ? 'text-red-400' : 'text-emerald-400'}> {heuristics.network}%
50 ? 'bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.5)]' : 'bg-emerald-500'}`} - style={{ width: `${heuristics.network}%` }} + className={`h-1.5 rounded-full transition-all duration-1000 ${heuristics.network > 50 ? 'bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.5)]' : 'bg-emerald-500'}`} + style={{ width: `${heuristics.network}%` }} />
@@ -226,36 +252,44 @@ const DumperPage = () => {
- VM Sandbox Score - 50 ? 'text-yellow-400' : 'text-emerald-400'}> + Threat Risk Score + 50 ? 'text-red-400 font-black' : 'text-emerald-400'}> {heuristics.suspicious}%
50 ? 'bg-yellow-500 shadow-[0_0_8px_rgba(234,179,8,0.5)]' : 'bg-emerald-500'}`} - style={{ width: `${heuristics.suspicious}%` }} + className={`h-1.5 rounded-full transition-all duration-1000 ${heuristics.suspicious > 50 ? 'bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.5)]' : 'bg-emerald-500'}`} + style={{ width: `${heuristics.suspicious}%` }} />
- -
+ +
{envLogs.length > 0 ? envLogs.map((log, i) => ( -
- +
+ {log}
)) : ( -

No environment hooks triggered

+

Waiting for VM telemetry...

)}
@@ -270,4 +304,4 @@ DumperPage.getLayout = function getLayout(page: ReactElement) { return {page}; }; -export default DumperPage; \ No newline at end of file +export default DumperPage;