准备工作
sliver客户端和服务端之间命令的通信采用的grpc, 服务端和和植入程序通信使用的protobuf,所以在开发之前需要了解 grpc和protobuf, 相关文档:
https://jergoo.gitbooks.io/go-grpc-practice-guide/content/chapter2/hello-grpc.html
https://jergoo.gitbooks.io/go-grpc-practice-guide/content/chapter1/protobuf.html
安装grpc protobuf相关工具:
https://jergoo.gitbooks.io/go-grpc-practice-guide/content/chapter1/install.html
定义自定义rpc服务
首先定义proto文件中的服务和相关数据类型:
在文件 sliver/protobuf/rpcpb/services.proto 增加PortScan方法:
rpc PortScan(sliverpb.PortScanReq) returns(sliverpb.PortScanResp);
然后在 sliver/protobuf/sliverpb/sliver.proto 增加 PortScan的请求参数和响应:
// 端口扫描请求参数
message PortScanReq {
string Targets = 1;
string Ports = 2;
commonpb.Request Request = 9;
}
// 端口扫描返回数据
message PortScanResp {
repeated string result = 1;
commonpb.Response Response = 9;
}
编译proto文件
在 sliver/Makefile 已经有写好的makefile, 直接执行make pb则可以自动重新编译.proto文件
如图所示,已经成功生成对应的go代码 sliver/protobuf/rpcpb/services_grpc.pb.go
在server端编写rpc服务go代码
新建文件 sliver/server/rpc/rpc-portscan.go, 写入以下代码:
package rpc
import (
"context"
"github.com/bishopfox/sliver/protobuf/commonpb"
"github.com/bishopfox/sliver/protobuf/sliverpb"
)
func (rpc *Server) PortScan(ctx context.Context, req *sliverpb.PortScanReq) (*sliverpb.PortScanResp, error) {
resp := &sliverpb.PortScanResp{Response: &commonpb.Response{}}
err := rpc.GenericHandler(req, resp)
if err != nil {
return nil, err
}
return resp, nil
}
这时候服务端定义就完成了
添加客户端命令
注意: 客户端和服务端命令是公用的,只需在客户端中定义相应命令即可
在文件 sliver/client/constants/constants.go 增加我们的命令port_scan
然后创建文件夹 sliver/client/command/portscan 新建portscan.go文件,加入以下代码:
package portscan
import (
"context"
"fmt"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/desertbit/grumble"
)
func PortScanCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
targets := ctx.Args.String("targets")
ports := ctx.Args.String("ports")
_, _ = con.Println(fmt.Sprintf("正在扫描 %s, 端口: %s", targets, ports))
resp, err := con.Rpc.PortScan(context.Background(), &sliverpb.PortScanReq{
Targets: targets,
Ports: ports,
Request: con.ActiveTarget.Request(ctx),
})
if err != nil {
_, _ = con.PrintErrorf("%s\n", err)
return
}
_, _ = con.Println("端口扫描完成, 结果: ")
for _, line := range resp.Result {
_, _ = con.Println(line)
}
}
然后在 sliver/client/command/commands.go 定义portscan命令
// 自定义端口扫描命令
con.App.AddCommand(&grumble.Command{
Name: consts.PortScanStr,
Help: "自定义端口扫描命令",
LongHelp: help.GetHelpFor([]string{consts.PortScanStr}),
Flags: func(f *grumble.Flags) {
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
},
Args: func(a *grumble.Args) {
a.String("targets", "待扫描的目标, 例:192.168.1.1,8.8.8.8")
a.String("ports", "待扫描的端口,例: 80,443,22", grumble.Default("80,443,22"))
},
Run: func(ctx *grumble.Context) error {
_, _ = con.Println()
portscan.PortScanCmd(ctx, con)
_, _ = con.Println()
return nil
},
HelpGroup: consts.SliverHelpGroup,
})
这时候客户端和服务端交互相关代码已经全部编写完毕了,执行make 重新编译客户端和服务端
这时候可以看到portscan命令已经成功加入进去了,但是还没有在植入程序实现具体的端口扫描功能,所以返回 rpc error: code = Unknown desc = unknown message type
在植入程序中实现portscan功能
修改 sliver/protobuf/sliverpb/constants.go 增加消息类型常量,根据植入程序收到的消息类型返回MsgPortScan
创建目录 sliver/implant/sliver/portscan/ 新建port_scan.go文件,代码如下
package portscan
import (
"fmt"
"github.com/panjf2000/ants/v2"
"net"
"strings"
"sync"
"time"
)
type Task struct {
Ip string
Port string
}
func isOpen(ip, port string) bool {
_, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", ip, port), 4*time.Second)
if err != nil {
return false
}
return true
}
func ScanPort(targets, ports string) []string {
scanTargets := strings.Split(targets, ",")
scanPorts := strings.Split(ports, ",")
var ret []string
var lock sync.Mutex
wg := new(sync.WaitGroup)
defer ants.Release()
p, _ := ants.NewPoolWithFunc(20, func(param interface{}) {
defer wg.Done()
task := param.(Task)
if isOpen(task.Ip, task.Port) {
lock.Lock()
ret = append(ret, fmt.Sprintf("%s:%s", task.Ip, task.Port))
lock.Unlock()
}
})
defer p.Release()
for _, target := range scanTargets {
for _, port := range scanPorts {
p.Invoke(Task{
Ip: target,
Port: port,
})
wg.Add(1)
}
}
wg.Wait()
return ret
}
然后在 sliver/implant/sliver/handlers/rpc-handlers.go 添加如下代码
// 端口扫描
func portscanHandler(data []byte, resp RPCResponse) {
sc := &sliverpb.PortScanReq{}
err := proto.Unmarshal(data, sc)
if err != nil {
// {{if .Config.Debug}}
log.Printf("error decoding message: %v", err)
// {{end}}
return
}
res := &sliverpb.PortScanResp{}
res.Result = portscan.ScanPort(sc.Targets, sc.Ports)
data, err = proto.Marshal(res)
resp(data, err)
}
如图所示,调用了porscan具体的功能,并将结果通过protobuf序列化并返回给服务端
然后需要将功能注册到对应的操作系统,例如我的功能只能在macos下运行,则需要在对应的文件下注册功能
sliver/implant/sliver/handlers/handlers_操作系统类型.go