daemon-sec-lotl

DÆMONBins: GTFOBins × LOLBAS × WADComs × LOOBins in one filterable catalog
git clone https://git.daemon-sec.xyz/daemon-sec-lotl.git
Log | Files | Refs | Submodules | README | LICENSE

enrich-data.mjs (4329B)


      1 import { readFileSync, writeFileSync } from 'node:fs';
      2 import { createHash } from 'node:crypto';
      3 import { fileURLToPath } from 'node:url';
      4 import { resolve, dirname } from 'node:path';
      5 import { validateTechniques, SOURCES, PLATFORMS, CAPABILITIES } from './technique-schema.mjs';
      6 
      7 const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
      8 const read = p => JSON.parse(readFileSync(resolve(root, p), 'utf8'));
      9 const uniq = a => [...new Set(a)];
     10 export const stableId = (prefix, value) => `${prefix}:${createHash('sha256').update(value).digest('hex').slice(0, 16)}`;
     11 
     12 export function normalizeTechnique(t) {
     13   const row = { ...t };
     14   // A Unix command is not evidence of macOS compatibility. Dedicated LOOBins
     15   // entries retain macOS; GTFOBins entries remain Linux pending per-command review.
     16   if (row.source === 'GTFOBins') {
     17     row.platform = row.platform.filter(p => p !== 'macOS');
     18     row.compatibility = 'Linux reference. macOS compatibility has not been checked for this command and execution context.';
     19   }
     20   const itemMap = { No_Creds: 'No credentials', AES_Key: 'AES key', Hash: 'NTLM hash', PFX: 'Certificate', TGT: 'Kerberos ticket', TGS: 'Kerberos ticket', powershell: 'PowerShell', PowerShell: 'PowerShell' };
     21   row.requires = uniq((row.requires || []).filter(x => !['PrivEsc', 'Exploitation', 'target', 'service'].includes(x)).map(x => itemMap[x] || x));
     22   row.services = uniq((row.services || []).filter(x => x !== 'Enumeration'));
     23   row.environment ||= /kubectl|crictl|^ctr$|runc|nerdctl|docker/i.test(row.toolName) ? ['Containers'] : row.platform.includes('ActiveDirectory') ? ['Active Directory'] : ['Local host'];
     24   row.verification ||= 'Upstream reference';
     25   row.availability ||= row.source === 'LOOBins' ? 'Built in' : row.source === 'WADComs' || row.toolId.startsWith('wadcoms:') ? 'Installed tool' : 'Check installation';
     26   return row;
     27 }
     28 
     29 export function enrich() {
     30   const previous = read('src/data/techniques.json');
     31   const existingTools = new Map(read('src/data/tools.json').map(t => [t.id, t]));
     32   const snapshot = read('src/data/sources/loobins.json');
     33   const additions = read('src/data/catalog-additions.json');
     34   const replaceIds = new Set([...snapshot.techniques, ...additions].map(t => t.id));
     35   const techniques = [...previous.filter(t => t.source !== 'LOOBins' && !replaceIds.has(t.id)), ...snapshot.techniques, ...additions].map(normalizeTechnique);
     36   const tools = new Map();
     37   for (const t of techniques) {
     38     const old = existingTools.get(t.toolId);
     39     t.aliases = uniq([...(t.aliases || []), ...(old?.aliases || [])]);
     40     if (!tools.has(t.toolId)) tools.set(t.toolId, { ...old, id: t.toolId, name: t.toolName, source: t.source, platform: [], references: [], count: 0 });
     41     const tool = tools.get(t.toolId);
     42     tool.platform = uniq([...tool.platform, ...t.platform]);
     43     tool.references = uniq([...tool.references, ...t.references]);
     44     tool.count++;
     45   }
     46   const problems = validateTechniques(techniques);
     47   if (problems.length) throw new Error(problems.join('\n'));
     48   const count = values => values.reduce((a, v) => ({ ...a, [v]: (a[v] || 0) + 1 }), {});
     49   const facets = read('src/data/facets.json');
     50   facets.platforms = PLATFORMS.filter(p => techniques.some(t => t.platform.includes(p)));
     51   facets.sources = SOURCES.filter(s => techniques.some(t => t.source === s));
     52   facets.capabilities = CAPABILITIES.filter(c => techniques.some(t => t.capability.includes(c)));
     53   facets.counts = { techniques: techniques.length, tools: tools.size, added: techniques.filter(t => t.added).length,
     54     bySource: count(techniques.map(t => t.source)), byPlatform: count(techniques.flatMap(t => t.platform)),
     55     byCapability: count(techniques.flatMap(t => t.capability)), toolsBySource: count([...tools.values()].map(t => t.source)) };
     56   facets.commits.loobins = snapshot.commit;
     57   for (const [file, data] of [['techniques', techniques], ['tools', [...tools.values()]], ['facets', facets]]) {
     58     writeFileSync(resolve(root, `src/data/${file}.json`), JSON.stringify(data, null, file === 'facets' ? 2 : 0) + '\n');
     59   }
     60   console.log(`Enriched ${techniques.length} techniques across ${tools.size} tools (${snapshot.techniques.length} LOOBins, ${additions.length} curated additions).`);
     61 }
     62 if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) enrich();