wormclaude 1.0.185 → 1.0.187
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/dist/pentest.js +55 -2
- package/dist/theme.js +1 -1
- package/dist/tools.js +41 -4
- package/package.json +1 -1
package/dist/pentest.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// WormClaude güvenlik tarama — İSTEMCİ (aptal HTTP göndericisi).
|
|
2
|
+
//
|
|
3
|
+
// Bu modül hiç payload/tespit/araç içermez. Sunucudan HTTP istek SPEC'leri alır, her birini
|
|
4
|
+
// YEREL fetch eder (trafik kullanıcının IP'sinden çıkar), ham cevabı sunucuya geri yollar.
|
|
5
|
+
// Tüm tarama zekası (payload, tespit, zincirleme) SUNUCUDA gizlidir. Dış binary YOK.
|
|
6
|
+
//
|
|
7
|
+
// Güvenlik: yalnız sunucunun bildirdiği allowed_hosts'a istek atılır (sunucuyu rastgele
|
|
8
|
+
// hedeflere SSRF proxy'si yapmayı engeller).
|
|
9
|
+
import net from 'node:net';
|
|
1
10
|
import { getLang } from './i18n.js';
|
|
2
11
|
const tt = (en, tr) => (getLang() === 'tr' ? tr : en);
|
|
3
12
|
const MAX_BODY = 262_144; // 256 KB cevap gövdesi üst sınırı
|
|
@@ -160,15 +169,59 @@ export function _mergeAuth(reqHeaders, auth) {
|
|
|
160
169
|
h[k] = v;
|
|
161
170
|
return h;
|
|
162
171
|
}
|
|
172
|
+
// Ham TCP connect (port taraması/banner). Bağlandıysa port AÇIK; gelen ilk veri = banner.
|
|
173
|
+
// allowed-host SSRF guard'ı burada da geçerli (yalnız sunucunun bildirdiği hedefe bağlan).
|
|
174
|
+
function execTcp(req, allowed) {
|
|
175
|
+
return new Promise((resolve) => {
|
|
176
|
+
const t0 = Date.now();
|
|
177
|
+
const host = (req.host || '').toLowerCase();
|
|
178
|
+
if (!allowed.has(host)) {
|
|
179
|
+
resolve({ id: req.id, ms: 0, error: `izin-dışı host: ${host}` });
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const sock = new net.Socket();
|
|
183
|
+
let banner = '', connected = false, done = false;
|
|
184
|
+
const finish = (extra) => {
|
|
185
|
+
if (done)
|
|
186
|
+
return;
|
|
187
|
+
done = true;
|
|
188
|
+
try {
|
|
189
|
+
sock.destroy();
|
|
190
|
+
}
|
|
191
|
+
catch { }
|
|
192
|
+
resolve({ id: req.id, ms: Date.now() - t0, body: banner.slice(0, 2048), status: connected ? 1 : 0, ...extra });
|
|
193
|
+
};
|
|
194
|
+
sock.setTimeout(Math.max(1, req.timeout || 4) * 1000);
|
|
195
|
+
sock.on('connect', () => { connected = true; if (req.send) {
|
|
196
|
+
try {
|
|
197
|
+
sock.write(req.send);
|
|
198
|
+
}
|
|
199
|
+
catch { }
|
|
200
|
+
} });
|
|
201
|
+
sock.on('data', (d) => { banner += d.toString('latin1'); if (banner.length >= 2048)
|
|
202
|
+
finish({}); });
|
|
203
|
+
sock.on('timeout', () => finish({})); // bağlandı ama banner yoksa: açık, banner boş
|
|
204
|
+
sock.on('error', () => finish({ status: 0, error: 'closed' })); // bağlanamadı → kapalı/filtreli
|
|
205
|
+
sock.on('close', () => finish({}));
|
|
206
|
+
try {
|
|
207
|
+
sock.connect(req.port || 0, req.host || '');
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
finish({ status: 0, error: 'connect-fail' });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
163
214
|
// Tek bir istek-spec'ini yerelde çalıştır. allowed dışı host → atlanır (SSRF koruması).
|
|
164
215
|
async function execReq(req, allowed, auth) {
|
|
216
|
+
if (req.type === 'tcp')
|
|
217
|
+
return execTcp(req, allowed);
|
|
165
218
|
const t0 = Date.now();
|
|
166
|
-
const h = hostOf(req.url);
|
|
219
|
+
const h = hostOf(req.url || '');
|
|
167
220
|
if (!allowed.has(h)) {
|
|
168
221
|
return { id: req.id, ms: 0, error: `izin-dışı host: ${h}` };
|
|
169
222
|
}
|
|
170
223
|
try {
|
|
171
|
-
const r = await fetch(req.url, {
|
|
224
|
+
const r = await fetch(req.url || '', {
|
|
172
225
|
method: req.method || 'GET',
|
|
173
226
|
headers: _mergeAuth(req.headers, auth),
|
|
174
227
|
body: req.body,
|
package/dist/theme.js
CHANGED
package/dist/tools.js
CHANGED
|
@@ -230,7 +230,7 @@ export const toolSchemas = [
|
|
|
230
230
|
parameters: {
|
|
231
231
|
type: 'object',
|
|
232
232
|
properties: {
|
|
233
|
-
tool: { type: 'string', enum: ['full', 'recon', 'scan', 'xss', 'sqli', 'dirscan', 'idor'], description: 'full → tam kapsamlı (recon+dirscan+xss+sqli+idor + tüm şablonlar
|
|
233
|
+
tool: { type: 'string', enum: ['full', 'recon', 'scan', 'xss', 'sqli', 'dirscan', 'idor', 'portscan'], description: 'full → tam kapsamlı web taraması (recon+dirscan+xss+sqli+idor + tüm şablonlar); recon/scan/dirscan/portscan → domain/host; xss/sqli/idor → parameterized URL. portscan = TCP port/servis taraması (açık portlar+banner; "port tara"/"açık portlar"/"hangi servisler açık" denince)' },
|
|
234
234
|
target: { type: 'string', description: 'Domain for recon/scan/dirscan (example.com) OR parameterized URL for xss/sqli (https://example.com/p?id=1)' },
|
|
235
235
|
cookie: { type: 'string', description: 'Optional. Session cookie for AUTHENTICATED / behind-login scanning, e.g. "sessionid=abc123; csrftoken=xyz". The user copies it from their logged-in browser (DevTools → Application → Cookies, or the Cookie request header). Sent only to the target from the user IP — never stored on the server. Provide it whenever the user wants to scan pages behind a login.' },
|
|
236
236
|
auth_header: { type: 'string', description: 'Optional. Authorization header value for token-based authenticated scanning, e.g. "Bearer eyJhbGci..." or "Basic dXNlcjpwYXNz". Use instead of / together with cookie when the site authenticates via a token header.' },
|
|
@@ -737,7 +737,7 @@ async function execOne(call, hooks) {
|
|
|
737
737
|
if (call.name === 'SecurityScan') {
|
|
738
738
|
let tool = String(args?.tool || '').toLowerCase().trim();
|
|
739
739
|
const target = String(args?.target || '').trim();
|
|
740
|
-
if (!['recon', 'scan', 'xss', 'sqli', 'dirscan', 'idor', 'full', 'fullscan'].includes(tool) || !target) {
|
|
740
|
+
if (!['recon', 'scan', 'xss', 'sqli', 'dirscan', 'idor', 'portscan', 'full', 'fullscan'].includes(tool) || !target) {
|
|
741
741
|
return { ok: false, output: 'SecurityScan: tool (recon|scan|xss|sqli|dirscan|full) ve target gerekli.', args };
|
|
742
742
|
}
|
|
743
743
|
if (tool === 'scan')
|
|
@@ -818,6 +818,31 @@ async function execOne(call, hooks) {
|
|
|
818
818
|
rpt += `\n[${_t}] hata: ${e?.message || e}`;
|
|
819
819
|
}
|
|
820
820
|
}
|
|
821
|
+
// TÜM-SUBDOMAIN TARAMASI: recon'un bulduğu alt-alanlarda da xss+sqli koş (sadece root değil →
|
|
822
|
+
// gerçek saldırı yüzeyi). Cap'li (top 5) + kullanıcı IP'sinden; ölü/erişilemez olan hızlı düşer.
|
|
823
|
+
const _rootHost = target.toLowerCase().replace(/^https?:\/\//, '').replace(/[/?].*$/, '').replace(/^www\./, '');
|
|
824
|
+
const _subs = [...new Set(all.filter((x) => x.type === 'subdomain' && x.value)
|
|
825
|
+
.map((x) => String(x.value).toLowerCase().trim()))]
|
|
826
|
+
.filter((s) => s && !s.startsWith('*') && s.includes('.') && s !== _rootHost && s !== 'www.' + _rootHost)
|
|
827
|
+
.slice(0, 5);
|
|
828
|
+
if (_subs.length && !hooks?.signal?.aborted) {
|
|
829
|
+
rpt += `\n\n[subdomain-sweep] ${_subs.length} alt-alan taranıyor (xss+sqli)…`;
|
|
830
|
+
for (const _sub of _subs) {
|
|
831
|
+
if (hooks?.signal?.aborted)
|
|
832
|
+
break;
|
|
833
|
+
for (const _st of ['xss', 'sqli']) {
|
|
834
|
+
try {
|
|
835
|
+
const o2 = await runPentest(cfg(), _st, `https://${_sub}/`, true, () => { }, _auth);
|
|
836
|
+
const ff = (o2?.ok ? (o2.report?.findings || []) : []).filter((x) => x.note !== 'no_params');
|
|
837
|
+
if (ff.length) {
|
|
838
|
+
all.push(...ff.map((x) => ({ ...x, scan: `${_st}@${_sub}`, subdomain: _sub })));
|
|
839
|
+
rpt += `\n [${_sub} ${_st}] ${ff.length} bulgu`;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
catch { }
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
821
846
|
// no_params spam'ini tekille (her inject tarayıcı bir tane ekliyor)
|
|
822
847
|
const _np = all.filter((f) => f.note === 'no_params');
|
|
823
848
|
for (let i = all.length - 1; i >= 0; i--)
|
|
@@ -828,13 +853,25 @@ async function execOne(call, hooks) {
|
|
|
828
853
|
evidence: `${_np.length} enjeksiyon tarayıcısı test edilebilir GET parametresi bulamadı — girdiler POST form/login-arkası/JS-üretimli olabilir.` });
|
|
829
854
|
const _sev = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
|
|
830
855
|
all.sort((a, b) => (_sev[a.severity] ?? 9) - (_sev[b.severity] ?? 9));
|
|
856
|
+
// CVSS 3.1 taban skoru (yaklaşık, tip-bazlı; yoksa severity-haritası). Profesyonel rapor için.
|
|
857
|
+
const _CVSS = { sqli: 9.8, nosql: 9.8, 'cmd-injection': 9.8, ssti: 9.4, ssrf: 8.6,
|
|
858
|
+
xxe: 8.6, lfi: 8.1, 'ldap-injection': 8.1, 'subdomain-takeover': 8.1, exposure: 7.5, idor: 6.5,
|
|
859
|
+
xss: 6.1, 'open-redirect': 6.1, crlf: 6.1, 'host-header': 6.1, cookie_flags: 5.3, cors: 5.3,
|
|
860
|
+
reflection: 4.3, weak_headers: 3.1 };
|
|
861
|
+
const _sevCvss = { critical: 9.5, high: 8.1, medium: 6.1, low: 3.5, info: 0 };
|
|
862
|
+
const _skipCvss = new Set(['info', 'tech', 'subdomain', 'host']);
|
|
863
|
+
for (const f of all) {
|
|
864
|
+
if (_skipCvss.has(f.type) || f.severity === 'info')
|
|
865
|
+
continue;
|
|
866
|
+
f.cvss = _CVSS[f.type] ?? _CVSS[String(f.scan || '').split('@')[0]] ?? _sevCvss[f.severity] ?? 0;
|
|
867
|
+
}
|
|
831
868
|
if (_wafSeen || /cloudflare|akamai|imperva/i.test(rpt)) {
|
|
832
869
|
rpt += `\n\n⚠️ WAF/CDN tespit edildi — payload'lar filtrelenmiş olabilir; "temiz" sonuçlar KESİN değil. Kesin sonuç için origin sunucuya (CDN arkası) doğrudan tara.`;
|
|
833
870
|
}
|
|
834
|
-
rpt += `\n\nToplam ${all.length} bulgu (${_steps.length}
|
|
871
|
+
rpt += `\n\nToplam ${all.length} bulgu (${_steps.length} tarayıcı${_subs && _subs.length ? ' + ' + _subs.length + ' alt-alan' : ''}).`;
|
|
835
872
|
if (all.length)
|
|
836
873
|
rpt += '\n' + all.slice(0, 80).map((x) => ' ' + JSON.stringify(x)).join('\n');
|
|
837
|
-
rpt += `\n[full-done] comprehensive scan (${_steps.length} scanners) ran. Do NOT call SecurityScan again — report
|
|
874
|
+
rpt += `\n[full-done] comprehensive scan (${_steps.length} scanners${_subs && _subs.length ? ' + ' + _subs.length + ' subdomains' : ''}) ran. Each finding has a "cvss" base score. Do NOT call SecurityScan again — report to the user in text: severity-ordered summary, include the CVSS score per finding, note subdomain findings, prioritized fixes, in their language.`;
|
|
838
875
|
const _now = Date.now();
|
|
839
876
|
_scanCache.set(_fk, { out: rpt, t: _now });
|
|
840
877
|
for (const _t of ['recon', 'xss', 'sqli', 'dirscan'])
|