sicario-red-team 0.5.9 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src-cli/nodes/breacher.js +7 -7
- package/src-cli/nodes/executor.js +33 -12
- package/src-cli/nodes/scout.js +40 -18
package/package.json
CHANGED
|
@@ -41,12 +41,11 @@ export async function runBreacher(elements, personaType = 'GENERAL') {
|
|
|
41
41
|
You are a specialized node in the Sicario Autonomous Swarm.
|
|
42
42
|
${personaContext}
|
|
43
43
|
|
|
44
|
-
### VULNERABILITY TARGETING (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Hunt for logic flaws by attempting to mutate these hidden values (e.g., set price/quantity to 0 or negative, change account roles to 'admin', or swap user session IDs).
|
|
44
|
+
### VULNERABILITY TARGETING (DOM SUPREMACY MODE):
|
|
45
|
+
1. PIERCE THE SHADOW: Elements marked as 'isShadow: true' are encapsulated. Developers often leave internal fields (like price or user_id) unvalidated within custom Web Components.
|
|
46
|
+
2. BEHAVIORAL MAPPING: Analyze elements with custom roles (role="button") or pointer cursors. These are modern SPA action points that bypass traditional form-crawling scanners.
|
|
47
|
+
3. BEHAVIORAL TRIGGERS: Identify the specific trigger selector required to submit the payload (e.g., a <div> with role="button" or an ID like #sync-profile).
|
|
48
|
+
4. THE SNIPER RULE: Always prioritize basic Parameter Tampering. Mutate hidden HTML inputs (type='hidden') related to pricing, IDs, or roles to 0, "", or admin before attempting complex JS exploitation.
|
|
50
49
|
|
|
51
50
|
### PHASE 1: REASONING (THINK OUT LOUD)
|
|
52
51
|
Before providing JSON, analyze the DOM elements according to your specific persona.
|
|
@@ -62,7 +61,8 @@ Return a VALID JSON object. DO NOT use "null".
|
|
|
62
61
|
"targetElement": "CSS selector or name",
|
|
63
62
|
"mutation": {
|
|
64
63
|
"selector": "The CSS selector for the hidden field or input to target",
|
|
65
|
-
"value": "The malicious value to inject (e.g. 0, -1, 'admin', '1e10')"
|
|
64
|
+
"value": "The malicious value to inject (e.g. 0, -1, 'admin', '1e10')",
|
|
65
|
+
"trigger": "The CSS selector for the trigger element to click (div[role='button'], #submit, etc.)"
|
|
66
66
|
},
|
|
67
67
|
"mitigation": "Code-level fix"
|
|
68
68
|
}
|
|
@@ -26,16 +26,34 @@ export async function runExecutor(targetUrl, breachReport, options = {}) {
|
|
|
26
26
|
if (selector && value !== undefined) {
|
|
27
27
|
console.log(pc.yellow(` [Executor] : Mutating ${selector} -> ${value}`));
|
|
28
28
|
|
|
29
|
-
//
|
|
30
|
-
|
|
29
|
+
// 1. Shadow-Piercing Mutation
|
|
30
|
+
const targetLocator = page.locator(selector);
|
|
31
|
+
await targetLocator.evaluate((el, v) => {
|
|
31
32
|
el.value = v;
|
|
32
|
-
// Trigger
|
|
33
|
+
// Trigger events to bypass React/Next.js state listeners
|
|
33
34
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
34
35
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
35
36
|
}, value);
|
|
36
37
|
|
|
37
|
-
//
|
|
38
|
-
|
|
38
|
+
// 2. The Wiretap: Listen for background API traffic (DOM Supremacy)
|
|
39
|
+
let interceptedNetworkTraffic = "";
|
|
40
|
+
const responseHandler = async (response) => {
|
|
41
|
+
const resType = response.request().resourceType();
|
|
42
|
+
if (resType === 'fetch' || resType === 'xhr') {
|
|
43
|
+
try {
|
|
44
|
+
const body = await response.text();
|
|
45
|
+
// Capture URL and condensed body to bridge the Asynchronous State gap
|
|
46
|
+
interceptedNetworkTraffic += `[${response.status()}] ${response.url().split('/').pop()}: ${body.substring(0, 500)} | `;
|
|
47
|
+
} catch (e) {
|
|
48
|
+
// Ignore opaque or failed body reads
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
page.on('response', responseHandler);
|
|
53
|
+
|
|
54
|
+
// 3. Behavioral Trigger Pull
|
|
55
|
+
const triggerSelector = mutation.trigger || 'button[type="submit"], input[type="submit"], [role="button"], button';
|
|
56
|
+
console.log(pc.red(` [Executor] : Pulling the trigger (${triggerSelector})...`));
|
|
39
57
|
|
|
40
58
|
await Promise.all([
|
|
41
59
|
// Wait for either navigation or network to go quiet
|
|
@@ -43,23 +61,26 @@ export async function runExecutor(targetUrl, breachReport, options = {}) {
|
|
|
43
61
|
page.waitForNavigation({ timeout: 10000 }).catch(() => {}),
|
|
44
62
|
page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {})
|
|
45
63
|
]),
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
// Use locator.click() to natively pierce Shadow DOM
|
|
65
|
+
page.locator(triggerSelector).first().click().catch(async () => {
|
|
66
|
+
// Fallback to legacy form submit
|
|
48
67
|
await page.$eval('form', f => f.submit()).catch(() => {});
|
|
49
68
|
})
|
|
50
69
|
]);
|
|
51
70
|
|
|
52
|
-
//
|
|
53
|
-
await new Promise(r => setTimeout(r,
|
|
71
|
+
// 4. Capture Window (Wait for async logic to resolve)
|
|
72
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
73
|
+
page.off('response', responseHandler);
|
|
54
74
|
|
|
55
|
-
//
|
|
56
|
-
const
|
|
75
|
+
// 5. Final Intelligence Accumulation
|
|
76
|
+
const domText = await page.innerText('body');
|
|
77
|
+
const serverEvidence = `| NETWORK TRAFFIC |\n${interceptedNetworkTraffic || "No AJAX traffic detected."}\n\n| DOM TEXT |\n${domText}`;
|
|
57
78
|
const screenshot = await page.screenshot({ type: 'jpeg', quality: 20 });
|
|
58
79
|
|
|
59
80
|
await browser.close();
|
|
60
81
|
return {
|
|
61
82
|
success: true,
|
|
62
|
-
evidence:
|
|
83
|
+
evidence: serverEvidence.substring(0, 3000), // Larger limit for network data
|
|
63
84
|
screenshot
|
|
64
85
|
};
|
|
65
86
|
} else {
|
package/src-cli/nodes/scout.js
CHANGED
|
@@ -79,29 +79,51 @@ export async function runScout(url, options = {}) {
|
|
|
79
79
|
const dismissButton = await page.$('button.close-dialog');
|
|
80
80
|
if (dismissButton) await dismissButton.click();
|
|
81
81
|
|
|
82
|
-
// 3. Extract the DOM
|
|
82
|
+
// 3. Extract the DOM (DOM Supremacy: Shadow Piercing & Behavioral Mapping)
|
|
83
83
|
const interactiveElements = await page.evaluate(() => {
|
|
84
|
-
const elements = document.querySelectorAll('button, input, a, form');
|
|
85
84
|
const extracted = [];
|
|
85
|
+
const processedNodes = new Set(); // Prevent duplicates from recursion
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
87
|
+
const walk = (root) => {
|
|
88
|
+
const nodes = root.querySelectorAll('*');
|
|
89
|
+
nodes.forEach(el => {
|
|
90
|
+
if (processedNodes.has(el)) return;
|
|
91
|
+
processedNodes.add(el);
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
93
|
+
const tag = el.tagName.toLowerCase();
|
|
94
|
+
const style = window.getComputedStyle(el);
|
|
95
|
+
|
|
96
|
+
// Behavioral Hooks
|
|
97
|
+
const isClickable = style.cursor === 'pointer' || el.getAttribute('onclick') || el.onclick;
|
|
98
|
+
const hasRole = ['button', 'link', 'input', 'form', 'checkbox', 'radio'].includes(el.getAttribute('role'));
|
|
99
|
+
const isInput = ['input', 'select', 'textarea', 'button'].includes(tag);
|
|
100
|
+
const isAnchor = tag === 'a';
|
|
101
|
+
const isHiddenInput = tag === 'input' && el.type === 'hidden';
|
|
102
|
+
const isVisible = el.offsetWidth > 0 && el.offsetHeight > 0;
|
|
103
|
+
|
|
104
|
+
if ((isVisible && (isInput || isAnchor || isClickable || hasRole)) || isHiddenInput) {
|
|
105
|
+
extracted.push({
|
|
106
|
+
tag,
|
|
107
|
+
id: el.id || null,
|
|
108
|
+
name: el.name || null,
|
|
109
|
+
type: el.type || null,
|
|
110
|
+
value: el.value || null,
|
|
111
|
+
role: el.getAttribute('role') || null,
|
|
112
|
+
isHighValueTarget: isHiddenInput && el.value !== '',
|
|
113
|
+
text: el.innerText ? el.innerText.trim().substring(0, 50) : null,
|
|
114
|
+
href: el.href || null,
|
|
115
|
+
isShadow: root !== document
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Shadow DOM: Pierce the boundary
|
|
120
|
+
if (el.shadowRoot) {
|
|
121
|
+
walk(el.shadowRoot);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
};
|
|
104
125
|
|
|
126
|
+
walk(document);
|
|
105
127
|
return extracted;
|
|
106
128
|
});
|
|
107
129
|
|