icarus-cmd 0.3.1
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/LICENSE +21 -0
- package/README.md +57 -0
- package/package.json +40 -0
- package/scripts/postinstall.mjs +9 -0
- package/src/core/codescan.js +121 -0
- package/src/core/detect.js +64 -0
- package/src/core/registry.js +52 -0
- package/src/core/resolvers.js +53 -0
- package/src/core/runner.js +94 -0
- package/src/core/setup.js +102 -0
- package/src/core/severity.js +35 -0
- package/src/core/store.js +82 -0
- package/src/core/target.js +5 -0
- package/src/index.js +425 -0
- package/src/report/html.js +166 -0
- package/src/tools/dalfox.js +42 -0
- package/src/tools/ffuf.js +45 -0
- package/src/tools/gobuster.js +44 -0
- package/src/tools/httpx.js +48 -0
- package/src/tools/katana.js +45 -0
- package/src/tools/metasploit.js +59 -0
- package/src/tools/naabu.js +59 -0
- package/src/tools/nikto.js +34 -0
- package/src/tools/nmap.js +96 -0
- package/src/tools/nuclei.js +51 -0
- package/src/tools/sqlmap.js +64 -0
- package/src/tools/subfinder.js +27 -0
- package/src/tools/wafw00f.js +33 -0
- package/src/tools/whatweb.js +40 -0
- package/src/tools/wpscan.js +58 -0
- package/src/tools/zap.js +73 -0
- package/src/ui/anim.js +59 -0
- package/src/ui/banner.js +49 -0
- package/src/ui/logo.js +20 -0
- package/src/ui/results.js +45 -0
- package/src/web/public/index.html +253 -0
- package/src/web/public/logo.png +0 -0
- package/src/web/server.js +141 -0
- package/tools/resolvers.txt +7 -0
- package/wordlists/common.txt +140 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Serveur web local d'Icarus : sert l'UI, expose une API REST et streame la
|
|
2
|
+
// sortie des scans en direct (NDJSON sur la réponse POST). Aucune dépendance
|
|
3
|
+
// externe : http natif uniquement.
|
|
4
|
+
|
|
5
|
+
import { createServer } from 'node:http';
|
|
6
|
+
import { readFileSync, existsSync, createReadStream } from 'node:fs';
|
|
7
|
+
import { join, dirname, extname, basename } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
import { TOOLS, PROFILES, getTool, toolStatus } from '../core/registry.js';
|
|
11
|
+
import { runTool } from '../core/runner.js';
|
|
12
|
+
import { effectiveTarget } from '../core/target.js';
|
|
13
|
+
import { allFindings, findingsForTarget, allScans, clearAll, targets, DATA_DIR } from '../core/store.js';
|
|
14
|
+
import { generateReport } from '../report/html.js';
|
|
15
|
+
|
|
16
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const PUBLIC = join(HERE, 'public');
|
|
18
|
+
const MIME = { '.html': 'text/html', '.css': 'text/css', '.js': 'text/javascript', '.json': 'application/json', '.svg': 'image/svg+xml' };
|
|
19
|
+
|
|
20
|
+
function send(res, code, body, type = 'application/json') {
|
|
21
|
+
res.writeHead(code, { 'Content-Type': type, 'Cache-Control': 'no-store' });
|
|
22
|
+
res.end(typeof body === 'string' || Buffer.isBuffer(body) ? body : JSON.stringify(body));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function readBody(req) {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
let data = '';
|
|
28
|
+
req.on('data', (c) => { data += c; });
|
|
29
|
+
req.on('end', () => { try { resolve(JSON.parse(data || '{}')); } catch { resolve({}); } });
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Schéma "public" des outils (avec options) pour que l'UI génère ses contrôles.
|
|
34
|
+
function toolsState() {
|
|
35
|
+
return toolStatus({ fresh: true }).map(({ tool, bin, available }) => ({
|
|
36
|
+
id: tool.id,
|
|
37
|
+
name: tool.name,
|
|
38
|
+
category: tool.category,
|
|
39
|
+
description: tool.description,
|
|
40
|
+
needs: tool.needs,
|
|
41
|
+
available,
|
|
42
|
+
bin: bin || null,
|
|
43
|
+
install: tool.install,
|
|
44
|
+
options: tool.options || [],
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function handleScan(req, res) {
|
|
49
|
+
const body = await readBody(req);
|
|
50
|
+
const target = String(body.target || '').trim();
|
|
51
|
+
const ids = Array.isArray(body.tools) ? body.tools : [];
|
|
52
|
+
const timeout = Number(body.timeout) || 0;
|
|
53
|
+
const toolOpts = body.toolOpts || {};
|
|
54
|
+
|
|
55
|
+
if (!target || !ids.length) { send(res, 400, { error: 'target et tools requis' }); return; }
|
|
56
|
+
|
|
57
|
+
res.writeHead(200, { 'Content-Type': 'application/x-ndjson', 'Cache-Control': 'no-store', 'X-Accel-Buffering': 'no' });
|
|
58
|
+
const emit = (ev) => res.write(JSON.stringify(ev) + '\n');
|
|
59
|
+
|
|
60
|
+
const avail = new Set(toolStatus().filter((s) => s.available).map((s) => s.tool.id));
|
|
61
|
+
const run = ids.filter((id) => getTool(id) && avail.has(id));
|
|
62
|
+
const skipped = ids.filter((id) => getTool(id) && !avail.has(id));
|
|
63
|
+
if (skipped.length) emit({ type: 'info', message: `Ignorés (non installés) : ${skipped.join(', ')}` });
|
|
64
|
+
|
|
65
|
+
let totalFindings = 0;
|
|
66
|
+
for (const id of run) {
|
|
67
|
+
const tool = getTool(id);
|
|
68
|
+
const et = effectiveTarget(tool, target);
|
|
69
|
+
emit({ type: 'tool-start', tool: id, name: tool.name, target: et });
|
|
70
|
+
|
|
71
|
+
const opts = { ...(toolOpts[id] || {}), timeout };
|
|
72
|
+
const result = await runTool(tool, et, {
|
|
73
|
+
opts,
|
|
74
|
+
onLog: (l) => emit({ type: 'log', tool: id, line: l }),
|
|
75
|
+
onData: (s) => emit({ type: 'out', tool: id, chunk: s }),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
totalFindings += result.findings.length;
|
|
79
|
+
emit({ type: 'tool-done', tool: id, code: result.code, error: result.error || null, findings: result.findings });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
emit({ type: 'done', totalFindings, tools: run.length });
|
|
83
|
+
res.end();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function handleApi(req, res, url) {
|
|
87
|
+
const path = url.pathname;
|
|
88
|
+
|
|
89
|
+
if (path === '/api/state' && req.method === 'GET') {
|
|
90
|
+
return send(res, 200, {
|
|
91
|
+
tools: toolsState(),
|
|
92
|
+
profiles: PROFILES,
|
|
93
|
+
targets: targets(),
|
|
94
|
+
findingsCount: allFindings().length,
|
|
95
|
+
scansCount: allScans().length,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (path === '/api/findings' && req.method === 'GET') {
|
|
99
|
+
const t = url.searchParams.get('target');
|
|
100
|
+
return send(res, 200, t ? findingsForTarget(t) : allFindings());
|
|
101
|
+
}
|
|
102
|
+
if (path === '/api/scans' && req.method === 'GET') return send(res, 200, allScans());
|
|
103
|
+
if (path === '/api/scan' && req.method === 'POST') return handleScan(req, res);
|
|
104
|
+
if (path === '/api/clear' && req.method === 'POST') { clearAll(); return send(res, 200, { ok: true }); }
|
|
105
|
+
if (path === '/api/report' && req.method === 'POST') {
|
|
106
|
+
const { htmlPath, jsonPath } = generateReport(allFindings(), { scans: allScans() });
|
|
107
|
+
return send(res, 200, { htmlUrl: `/report/${basename(htmlPath)}`, jsonUrl: `/report/${basename(jsonPath)}`, htmlPath });
|
|
108
|
+
}
|
|
109
|
+
return send(res, 404, { error: 'not found' });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function serveStatic(res, file) {
|
|
113
|
+
if (!existsSync(file)) { send(res, 404, 'Not found', 'text/plain'); return; }
|
|
114
|
+
const type = MIME[extname(file)] || 'application/octet-stream';
|
|
115
|
+
res.writeHead(200, { 'Content-Type': type, 'Cache-Control': 'no-store' });
|
|
116
|
+
createReadStream(file).pipe(res);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function startWeb({ port = 7373 } = {}) {
|
|
120
|
+
const server = createServer(async (req, res) => {
|
|
121
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
122
|
+
try {
|
|
123
|
+
if (url.pathname.startsWith('/api/')) return await handleApi(req, res, url);
|
|
124
|
+
|
|
125
|
+
if (url.pathname.startsWith('/report/')) {
|
|
126
|
+
const f = join(DATA_DIR, 'reports', basename(url.pathname));
|
|
127
|
+
return serveStatic(res, f);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Fichiers statiques de l'UI.
|
|
131
|
+
const rel = url.pathname === '/' ? 'index.html' : url.pathname.replace(/^\//, '');
|
|
132
|
+
return serveStatic(res, join(PUBLIC, basename(rel)));
|
|
133
|
+
} catch (err) {
|
|
134
|
+
send(res, 500, { error: err.message });
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return new Promise((resolve) => {
|
|
139
|
+
server.listen(port, () => resolve({ server, url: `http://localhost:${port}` }));
|
|
140
|
+
});
|
|
141
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
admin
|
|
2
|
+
administrator
|
|
3
|
+
login
|
|
4
|
+
logout
|
|
5
|
+
register
|
|
6
|
+
signup
|
|
7
|
+
signin
|
|
8
|
+
dashboard
|
|
9
|
+
panel
|
|
10
|
+
cpanel
|
|
11
|
+
wp-admin
|
|
12
|
+
wp-login.php
|
|
13
|
+
wp-content
|
|
14
|
+
wp-includes
|
|
15
|
+
wp-json
|
|
16
|
+
api
|
|
17
|
+
api/v1
|
|
18
|
+
api/v2
|
|
19
|
+
graphql
|
|
20
|
+
rest
|
|
21
|
+
backup
|
|
22
|
+
backups
|
|
23
|
+
old
|
|
24
|
+
new
|
|
25
|
+
test
|
|
26
|
+
testing
|
|
27
|
+
dev
|
|
28
|
+
staging
|
|
29
|
+
prod
|
|
30
|
+
config
|
|
31
|
+
configuration
|
|
32
|
+
settings
|
|
33
|
+
setup
|
|
34
|
+
install
|
|
35
|
+
installer
|
|
36
|
+
phpinfo.php
|
|
37
|
+
info.php
|
|
38
|
+
server-status
|
|
39
|
+
server-info
|
|
40
|
+
.git
|
|
41
|
+
.git/config
|
|
42
|
+
.env
|
|
43
|
+
.env.local
|
|
44
|
+
.htaccess
|
|
45
|
+
.htpasswd
|
|
46
|
+
.svn
|
|
47
|
+
.ds_store
|
|
48
|
+
robots.txt
|
|
49
|
+
sitemap.xml
|
|
50
|
+
crossdomain.xml
|
|
51
|
+
security.txt
|
|
52
|
+
.well-known
|
|
53
|
+
uploads
|
|
54
|
+
upload
|
|
55
|
+
files
|
|
56
|
+
file
|
|
57
|
+
images
|
|
58
|
+
img
|
|
59
|
+
assets
|
|
60
|
+
static
|
|
61
|
+
public
|
|
62
|
+
private
|
|
63
|
+
tmp
|
|
64
|
+
temp
|
|
65
|
+
cache
|
|
66
|
+
logs
|
|
67
|
+
log
|
|
68
|
+
db
|
|
69
|
+
database
|
|
70
|
+
sql
|
|
71
|
+
dump
|
|
72
|
+
data
|
|
73
|
+
storage
|
|
74
|
+
media
|
|
75
|
+
includes
|
|
76
|
+
inc
|
|
77
|
+
lib
|
|
78
|
+
libs
|
|
79
|
+
vendor
|
|
80
|
+
node_modules
|
|
81
|
+
src
|
|
82
|
+
js
|
|
83
|
+
css
|
|
84
|
+
fonts
|
|
85
|
+
download
|
|
86
|
+
downloads
|
|
87
|
+
docs
|
|
88
|
+
doc
|
|
89
|
+
documentation
|
|
90
|
+
help
|
|
91
|
+
support
|
|
92
|
+
contact
|
|
93
|
+
about
|
|
94
|
+
user
|
|
95
|
+
users
|
|
96
|
+
account
|
|
97
|
+
accounts
|
|
98
|
+
profile
|
|
99
|
+
profiles
|
|
100
|
+
member
|
|
101
|
+
members
|
|
102
|
+
auth
|
|
103
|
+
oauth
|
|
104
|
+
token
|
|
105
|
+
session
|
|
106
|
+
sessions
|
|
107
|
+
cart
|
|
108
|
+
checkout
|
|
109
|
+
order
|
|
110
|
+
orders
|
|
111
|
+
payment
|
|
112
|
+
payments
|
|
113
|
+
invoice
|
|
114
|
+
invoices
|
|
115
|
+
report
|
|
116
|
+
reports
|
|
117
|
+
export
|
|
118
|
+
import
|
|
119
|
+
search
|
|
120
|
+
query
|
|
121
|
+
status
|
|
122
|
+
health
|
|
123
|
+
healthz
|
|
124
|
+
ping
|
|
125
|
+
metrics
|
|
126
|
+
debug
|
|
127
|
+
console
|
|
128
|
+
shell
|
|
129
|
+
cmd
|
|
130
|
+
phpmyadmin
|
|
131
|
+
adminer
|
|
132
|
+
pma
|
|
133
|
+
mysql
|
|
134
|
+
manager
|
|
135
|
+
management
|
|
136
|
+
portal
|
|
137
|
+
internal
|
|
138
|
+
intranet
|
|
139
|
+
secret
|
|
140
|
+
hidden
|