Argus-Sliver 是首个基于 Web 端的 Sliver C2 二次开发平台。它利用 Vue3 + FastAPI + sliver-py 构建,旨在为安全研究人员提供一个直觉化、全功能的图形化渗透指挥中心
目前项目仍处于持续开发阶段,现阶段大部分接口逻辑由 AI 辅助生成,部分接口在稳定性和完整性方面仍存在不足,个别功能可能存在缺陷或尚未完全实现,后续将逐步进行优化和完善
- Sliver Server: 确保服务器已启动并开启
multiplayer模式。 - Python: 3.11 或更高版本。
- Node.js: 20+ (推荐使用 LTS 版本)。
cd backend
pip install -r requirements.txt
# 修改 backend/config.json 中的 sliver_config_path 为您的 .cfg 文件路径
# 然后运行后端 API 服务 (默认端口 8000)
python api.pycd frontend
npm install
npm run dev访问 http://localhost:5173 即可进入指挥中心。
Argus-Sliver/
├── backend/ # FastAPI 后端,处理 RPC 转接与 WS
│ ├── api.py # 路由注册与启动
│ ├── router_shell.py # WebSocket 交互式 Shell 逻辑
│ └── core.py # 状态管理与 SliverClient 封装
└── frontend/ # Vue3 前端
├── src/components/ # UI 组件库
└── src/App.vue # 主布局逻辑
Sliver源码体量较大,排除第三方库的go文件都有几千个,不过我们开发的内容就只需要改特定的地方即可,要是了解整个代码逻辑还是比较难的
简单演示添加一个内网主机探测扫描的功能
如果修改编译的源码必须是会修改生成.proto,.pb.go
版本必须如下:不然会有运行可能会报错
- 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/ |
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服务定义中增加 PortScan 方法
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
目录结构
| 目录 / 文件 | 作用简介 |
|---|---|
main.go |
Server 入口,解析参数并启动守护进程 |
assets/ |
内嵌资源(默认配置、模板、脚本等) |
builder/ |
Payload 构建调度与构建产物管理 |
c2/ |
C2 服务编排(监听器、任务分发、staging) |
certs/ |
TLS 证书生成与管理 |
cli/ |
Server CLI 启动逻辑 |
codenames/ |
Implant / Operator 代号生成 |
configs/ |
配置文件加载、校验与默认值处理 |
console/ |
管理控制台通用代码 |
core/ |
Server 核心运行时与状态管理 |
cryptography/ |
服务端加密与密钥管理 |
daemon/ |
守护进程生命周期管理 |
db/ |
数据库访问层(迁移、适配、查询) |
encoders/ |
服务端编码器管理与校验 |
generate/ |
代码生成与模板工具 |
gogo/ |
gogo/protobuf 兼容与定制 |
handlers/ |
RPC / 事件处理器 |
log/ |
日志系统与结构化日志配置 |
loot/ |
Loot(回传数据)存储与管理 |
msf/ |
Metasploit 集成相关逻辑 |
netstack/ |
用户态网络栈(基于 gVisor) |
rpc/ |
gRPC 服务实现与注册 |
sgn/ |
Sliver Guard Node(SGN)协调逻辑 |
transport/ |
C2 传输层与监听器管理 |
watchtower/ |
后台任务、监控与调度 |
website/ |
Web 静态资源与服务端处理 |
添加文件 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/command/文件下的开发格式大概如下:
commands.go– 注册信息导向型命令并将其绑定到控制台info.go– 查询详细的会话或信标元数据,并打印丰富的状态表实际的文件.go– 向植入体发送 ping 请求,以测试连接性和往返延迟
因此需要创建两个文件:commands.go 与具体功能实现文件
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}
}在创建一个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
上的客户端和环境就写好了然后编译运行
make
sliver/protobuf/sliverpb/constants.go添加连接的常量
// ===== ICMP Scan =====
MsgICMPScan
然后在添加
case *ICMPScanReq:
return MsgICMPScan添加一个icmp的扫描功能
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
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/下的文件的介绍:
extensions-wasm.go– 将与扩展相关的 RPC 消息路由到 WASM 运行时handlers-wireguard.go– 处理 WireGuard 控制消息和状态更新handlers.go– 注册核心处理器函数和共享分发工具handlers_darwin.go– macOS 特定的处理器绑定和功能开关handlers_generic.go– 无需平台特化时使用的通用处理器实现handlers_linux.go– 面向 Linux 的处理器逻辑和构建标签handlers_windows.go– Windows 平台特有的处理程序实现kill-handlers.go– 以平台无关的方式处理会话和任务的终止命令kill-handlers_windows.go– Windows 平台特有的终止命令处理pivot-handlers.go– 管理与 Pivot 相关的消息和隧道设置指令rpc-handlers-cgo.go– 适用于需要 CGO 的平台的 RPC 处理程序变体rpc-handlers-generic.go– 跨构建共享的通用 RPC 处理程序实现rpc-handlers.go– 核心 RPC 处理程序注册和分发循环rpc-handlers_darwin.go– macOS 平台特有的 RPC 处理程序调整rpc-handlers_linux.go– Linux RPC 处理程序自定义rpc-handlers_windows.go– Windows RPC 处理程序自定义tun-rportfwd.go– 处理反向端口转发通道特有的隧道消息tun.go– 处理通用隧道控制消息并协调链路状态
然后在implant/sliver/handlers/handlers/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 {
return
}
// 调用扫描逻辑
alive := hostscan.PerformIcmpScan(req.Range, int32(req.Timeout))
response := &sliverpb.ICMPScanResp{
AliveHosts: alive,
}
out, _ := proto.Marshal(response)
resp(out, nil)
}系统注册
sliverpb.MsgICMPScanReq: hostScanHandler,
编译运行
多人模式允许多个运营商(Operator)连接到同一 Sliver Server
场景说明:
- Sliver Server 跑在一台 VPS
- 多个红队成员在自己电脑上
- 每个人用一个operator profile 连接
架构如下:
┌──────────────────┐ C2
│ │ Protocol ┌─────────┐
│ Sliver C2 Server ├─────────────►│ Implant │
│ │ └─────────┘
└──────────────────┘
▲
│
gRPC/mTLS │
┌────────────┬────────┴─────┬───────────┐
│ │ │ │
┌─────┴──┐ │ │ ┌──┴─────┐
│Windows │ ┌────┴───┐ ┌────┴───┐ │Windows │
│Operator│ │Linux │ │MacOS │ │Operator│
└────────┘ │Operator│ │Operator│ └────────┘
└────────┘ └────────┘
启动server版:
./sliver-server_linux-amd64sliver > new-operator --name admin1 --lhost 127.0.0.1 --permissions all| 参数 | 含义 |
|---|---|
--name |
操作员名称 |
--lhost |
Sliver Server 地址(IP / 域名) |
--permissions all |
允许访问所有 gRPC API(最高权限) |
然后启动multiplayer
使用客户端连接
命令:
./sliver-client_linux-amd64 import /home/zss/.storage/sliver/admin1_127.0.0.1.cfg
./sliver-client_linux-amd64官方已经提供了直接调用服务的RPC服务可以用python直接调用
安装库如下:
pip install sliver-py测试连接代码如下:
调用前服务端要开启RPC命令如下:
sliver > new-operator --name admin1 --lhost 127.0.0.1 --permissions all
sliver > multiplayer
测试代码:
#!/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())#!/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如下:
#!/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())看一下结果
本工具仅限于合法的安全研究、安全审计及教育目的。用户在行使相关行为时应遵守当地法律。开发者不承担因滥用本工具而产生的任何直接或间接法律责任。
如果您觉得本项目有价值,欢迎点一个 Star ⭐!





































