关于DFIR – Velociraptor

发布于 2023-03-15  80 次阅读


资料来源:

顺丰网络安全DFIR平台实践之路

https://github.com/Velocidex/velociraptor

Velociraptor是一种免费的开源数字取证和响应平台,可以帮助安全专家快速识别和解决网络安全问题。它提供了一套高效的工具,可以自动化和简化常见的取证和响应任务,同时提供了可视化的仪表板和数据导出功能,以便用户更好地分析和理解数据。”””

根据官方描述,velociraptor主要提供数据的存储服务(json),而数据的处理(例如evtx文件处理为json格式发送的过程是在断点完成的),对于服务器的处理量可以大大降低而提高数据的查询效率

docker部署

docker的部署十分方便

  • git clone <https://github.com/weslambert/velociraptor-docker>
  • cd velociraptor-docker
  • 根据需要更改.env中的凭据值(admin/admin)
  • docker-compose up

GUI配置部署

在通过velociraptor.exe gui 进行部署时,会在输出的前几行显示出配置文件,默认的web root为admin/password

通过velociraptor.exe —config C:\\Users\\<CONFIG_PATH.yaml> user matoujin —role admin来添加各种角色的用户

通过velociraptor.exe gui —database <DIR_NAME> 指定本地目录来保存notebook笔记以及数据,否则会在temp目录中被默认销毁。以此实现多个database在本地的维护

离线收集(指无法部署客户端的情况)

https://docs.velociraptor.app/training/

通过VQL实现在端点进行信息收集

(貌似有个坑,docker下并不会打包一个自定义的offline collector,我没有在任何地方找到它)

(macos下的gui会在离线collector拉取github镜像的时候卡住,离线模式不可用)

目前在windows通过gui可以实现不报错()

netshare

采用RCE方案

wmic /node:”WIN” /user:Administrator process call create “C:\velo.exe”

关于VQL

VQL并不对Client做真正的Query

运行vql的三种方式: Command Line: velociraptor.exe -v query “select * from info()”

Command line in Artifact :

notebook功能:

notebook支持markdown以及VQL语句,和notion的block一样,使用cell进行区分

SELECT X,Y,Z FROM plugin(arg=1) WHERE X=1

SELECT <Column Selectors> FROM <VQL Plugin with Args> WHERE <Filter Condition>

关于第二个可选参数,由于VQL中并没有真正的“表”,只有从插件中获取的信息集

插件的定义:插件可以看作“行”(信息)的总和;插件允许参数:强类型,允许嵌套;

Columns Selectors中支持log函数:

SELECT OS, log(message=”I ran” From info()

可以达到输出日志的效果。由于生命周期lazy evaluation机制,如果where的条件没有达成,log函数(即Column Selectors)也不会发生“查询”或者理解为“执行”。如果where(Filter Condition)涉及到了取用Column Selectors中函数值,那么Selectors的函数仍然会发生执行。(Filter Condition中的相关条件需要前置)

变量与Foreach

全局变量

LET var = 1

获取自身进程的情况并获取所有列

SELECT * FROM pslist(pid=getipid())

查看目标文件的情况并获取所有列

SELECT * FROM stat(filename=”C:\\”)

SELECT * FROM forearch (
row={
	select Name, CommandLine, Exe FROM pslist(pid=getpid()) 
},query={
	SELECT Btime,Mtime,FullPath FORM stat(filename=Exe)
})

row作为子范围数据集,query作为从数据集中提取数据,并将只返回当前pid所运行的Exe的创建时间、修改时间、绝对路径.

glob(globs=””) 是对搜索文件的使用方法,基于通配符

SELECT FullPath ,hash(path=FullPath) 
FROM glob(globs=”C:/Windows/System32/*”) 
WHERE NOT IsDir

如果加上foreach的应用。scope()返回当前作用域,可以理解为不做任何操作的NOP

SELECT * FORM foreach(
row = {
	select FullPath from glob(globs="C:/Windows/System32/*")
	where NOT IsDir
}, query = {
	select FullPath , hash(FullPath) from scope() 
}) limit 10

关于hash()函数,它会返回MD5、SHA1、SHA256三种哈希值组成的json,只需要将hash().SHA256就可以得到对应的text类型值,样例如下

select FullPath , hash(FullPath).SHA256 from scope()

select FullPath , hash(FullPath).SHA256 AS SHA256 from scope()

select FullPath , hash(FullPath).SHA256 AS SHA256 from scope() where SHA256 == ”abc“

并且路径支持模糊匹配(?),例如

FullPath = “.png”可以直接匹配到png后缀

关于LET表达式

通过LET表达式实现了存储查询,LET只实现了QL语句的存储,并不实现QL的实际检索,符合Lazy evaluation

LET processes = select Name, CommandLine, Exe FROM pslist() 
LET times = SELECT Btime,Mtime,FullPath FROM stat(filename=Exe)

SELECT * FROM foreach(
	row=processes,query=times
)

如果要实现LET的存储,可以使用< =

LET processes <= select Name, CommandLine, Exe FROM pslist() 

如果实现LET的传参功能(方法):

LET TargetPid(pid) = pslist(pid) 
SELECT  Name, CommandLine, Exe FROM TargetPid(pid = 12345)

另,VQL并不会进行强制类型转换。

例子:如何创建工件来检测wmic process call create cmd.exe ?

wmic实际上是调用了System32中的cmd.exe

首先要检查ps进程,由于wmic可以执行命令,所以要查看CommandLine和exe

select Name , Pid, Ppid, CommandLine, Exe,* from pslist() 
LIMIT 5

由于创建新的进程并调用命令行相当于CommandLine执行cmd.exe

select Name , Pid, Ppid, CommandLine, Exe,* from pslist()
WHERE CommandLine =~ "cmd.exe"
LIMIT 5

然后查询父进程的名称,找到恶意进程

select Name , Pid, Ppid, 
{select Name FROM pslist(Pid=Ppid)} AS PARENTNAME,
CommandLine, Exe,* from pslist()
WHERE CommandLine =~ "cmd.exe" and PARENTNAME =~ "WmiPrvSE"
LIMIT 5

VQL-Artifacts

Artifact就是VQL和yaml的集合

通过

的铅笔键,可以修改Artifacts并进行自定义

name:确保了每个artifact不被覆盖

description:注释

type:CLIENT/CLIENT_EVENT/SERVER/SERVER_EVENT

client_event和server_event成对作用于端点控制。

parameters:运行工件时的参数(默认string)

sources-precondition:运行query的前提条件,如果precondition不满足,则不进行下面的query

parameters:
	- name: ProcessRegex
		default: cmd.exe

sources:
	- precondition:
		SELECT OS FROM info() where OS = 'windows'
		query: |
				select Name , Pid, Ppid, 
				{select Name FROM pslist(Pid=Ppid)} AS PARENTNAME,
				CommandLine, Exe,* from pslist()
				WHERE CommandLine =~ ProcessRegex and PARENTNAME =~ "WmiPrvSE"
				

如果存在语法错误,会在保存后报错。

在使用工件时,可以使用通配符“.”作为参数

Exercise 1

//format进行格式化字符串输出
SELECT format(format="%v:%v", args=[Laddr.IP,Laddr.Port]),pid,Timestamp,{
	//进程信息
	SELECT Exe, parse_pe(file=Exe).VersionInformation AS VersionInformation,
		//解析PE文件,获得制作商信息(恶意文件分析技巧)
		authenticode(filename=Exe),
		//exe文件的签名者
		Username,CommandLine FROM pslist(pid=PID)
		//进程信息
} AS ProcessInfo,{
	//dll信息通过modules函数获取
	SELECT ExePath From modules(pid=Pid)
} AS LinkedDlls
FROM netstat() 
//获取网络连接信息
where status =~ "Listen"

如果涉及到时间戳不对的问题,可以在工件中添加一个参数,

- name: DataBefore
	type: timestamp

然后在参数设置中设置为localhost,就可以使用本地时间

log()

在任何操作的前面加上log()都会输出日志

SELECT * FROM pslist() 
WHERE log(message=format(format="%T %v", args=[timestamp(now(), Name])) AND Username =~ "mk"

VQL call artifacts

SELECT * FROM Artifact.Windows.System.Pslist()

VQL IF

SELECT  * FROM IF(
	condition=<sub query or value>,
	then={      <sub query or value>        },
	else={      <sub query or value>        } 
SELECT  * FROM SWITCH(
	a={      <sub query or value>        },
	b={      <sub query or value>        }
)
SELECT  * FROM CHAIN(
	a={      <sub query or value>        },
	b={      <sub query or value>        }
)
//顺序执行所有子查询,结合所有返回列

Aggregate func

实现多行的收集,具有内部状态

count()(可以实现group by、order by函数)

事件查询

事件查询实现监控架构

SELECT * FROM clock()

VQL取证

?sram

文件查找:glob()

对斜杠不区分

**表示递归搜索 C:\user\**\*.exe,默认递归10min后取消

SELECT * FROM glob(globs=’’’C:\\user\\**\\*.exe’’’)

‘’‘原始字符串解决转义斜杠问题

(无法避免软连接递归的问题。造成资源浪费)

SELECT * FROM glob(globs=[’’’C:\\user\\**\\*.exe’’’],[’’’C:\\user\\**\\*.dll’’’])

glob只匹配文件名,不对文件类型进行校验,需要自行校验模块

文件访问系统

SELECT * FROM glob(globs=’’’/*’’’, accessor="register")

这种情况下,就将注册表作为根目录进行文件访问

SELECT * FROM glob(globs=’’’/C:/*’’’, accessor="ntfs")

如果文件路径带有特殊字符,与查询列名冲突,可以用反引号包裹表示为列名

//查询用户登录
SELECT  * FROM glob(globs='''/HKEY_CURRENT_USER/**/RUN/*''',accessor="reg")

SELECT Name,ModTime,

常用文件正则

parse_string_with_regex(string=Data.value,,reg='^”([^”]+)')

parse_string_with_regex(string=Data.value,,reg=['^”([^”]+)', '^([^" ]+ ?',)

如果存在环境变量

expand(path=<PATH_WITH_ENV>)

LET X = SELECT Name,FullPath,ModTime, Data.value AS StartupPath, 
	parse_string_with_regex(string=Data.value,,reg=['^”([^”]+)', '^([^" ]+ ?',) AS Parse 
	FROM glob(globs='''/HKEY_CURRENT_USER/**/RUN/*''',accessor="reg")

SELECT Name, FullPath,ModTime,StartupPath,hash(path=expand(path=Parse,g1)) AS Hash
FROM X

对字符串的hash(对accessor的运用)

SELECT hash(path=”hello world”, accessor=”data”) FORM scope()

还有url函数的使用,同样使用accessor

yara

example:未知恶意软件分析

SELECT * FROM pslist() WHERE Name =~ "Chrome"

SELECT * FROM handles(pid=112312)
where type =~ "File"

//找到了chrome打开的文件
SELECT * FROM foreach(
row={SELECT * FROM pslist() WHERE Name =~ "Chrome"},
query={
	SELECT * FROM handles(pid=Pid)
	where type =~ "File"
}
)

//找到url
LET YaraRule = '''
	rule URL {
		Strings : $a = /https?:\\/\\/[a-z0-9]\\/+&#:\\?.-]+/i
		condition: any of them
}
'''

SELECT * FROM foreach(
row={
SELECT  FullPath FROM glob(globs=''C:\\User\\**\\Chrome\\User Data\\Default\\**')
WHERE NOT IsDir
},
query={
//content用于获得上下文
	SELECT str(String.Data) AS URL FROM yara(files=Fullpath,rules=YaraRule,number=10000,content=10)
}

MFT NTFS

攻击痕迹取证

prefetch pf文件

工件:WIndows.Froensics.Prefetch

pf的时间戳最多存储8次,但是runcount可以记录程序运行次数

工件:WIndows.Timeline.Prefetch

每个时间戳都会占用一行

eventlog

SELECT * FROM parse_evtx(”C:\\system.evtx”)

evtx-data(github)dump所有evtx到sqlite中

工件:Windows.Eventlogs.EvtxHunter

evtx日志的开启情况其实是通过修改注册表获取

也就是说通过读取所有日志对应注册表键值就可以知道所有evtx的日志开启情况

select basename(path=dirname(path=FullPath)), Data.value From glob(globs=”HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Channels\\*\\Enabled”, accessor=”registry”)

dirname只返回路径(去除键名),basename只取出路径的最后一截

获取注册表所有键值

select * From read_reg_key(globs=”HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Channels\\*\\”)

etw event_tracer

etw是由provider集合的,负责事件监督

terminial : logman query providers

查看所有provider以及guid

logman query providers Microsoft-WIndows-DNS-client

查看对应provider的事件关键字(事件类型)、告警类型、应用调用provider情况

select * FROM watch_etw(guid=”{1C95126E-7EEA-49A9-A3FE-A378B03DDB4D}”) Where System.ID=3006

watch_etw是可以取代watch_evtx的,evtx本质上也是由etw provider提供支持的,所以只需要将evtx日志中的provider和guid找到,通过事件类型查询就可以得到etw_watc同样的效果

sysmon tracer

工件:WIndows.Sysinternals.SysmonLogForward

SysmonLogForward在VQL层上也是通过调用watch_etw来实现的,因为即使在sysmonlog在客户端被关闭的情况下,etw仍然工作,sysmon仍然可以得到日志

volatile state 易受攻击主机判断

内存分析并不是一个最佳方案(内存的镜像的存储和内存实际变化速度不匹配)

恶意软件需要确保它只有一个副本在运行。一个常见的方法是使用mutex互斥锁(或named mutex)创建一个具有常量名称的突变体:如果命名的突变体已经存在,则退出确保仅运行单个副本。

mutex监测

工件:Windows.Detection.Mutants

环境变量

工件:Windows.Detection.EnvironmentVariables

获取所有进程的环境变量

进程链

工件:Generic.System.Pstree

where Callchain=~ “cmd.+wmi”

支持通配符

内存映射

example:powershell是否发出http请求

SELECT * FROM foreach(
	row={
			SELECT * FROM pslist() WHERE Name=~ "powershell"
	},
	query={
		SELECT * FROM vad(pid=PID) WHERE MappingName=~"winhttp.dll"
	}
)

端点文件采集

端点文件采集,更倾向于保存端点的当前状态

Kap

纯粹的文件收集引入数据,需要通过解析上传文件才能进行分析

example:

SELECT * FROM foreach(
	row = {
			SELECT * FROM uploads(
			client_id='C.xxxxx',flow_id='F.xxxx') 
			WHERE vfs_path =~ "evtx"
	},query={
	//已经上传到的服务器的文件要加file_stone来转换文件系统
		SELECT * FROM parse_evtx(filename=file_stone(vfs_path))
})

AutoRuns

工件:Windows.Sysintelnets.AutoRuns

第三方工件

#1 额外工件

CCXlabs实验室提供额外工件,在工件界面直接导入

CCXDigger/artifacts/CyberCX/Windows at master · CCXLabs/CCXDigger

#2 yara规则拓展

https://malpedia-caad-fkie-fraunhofer-de.translate.goog/details/win.cobalt_strike?_x_tr_sl=auto&_x_tr_tl=zh-CN&_x_tr_hl=zh-CN

malpeida提供检查一些签名监测,使用yara并且带入到vql进行扫描,可以使用processmemory工件进行监测

进程分析框架:

第一步找到所有进程(符合条件)

LET processes = SELECT Name as ProcessName ,CommandLine ,Pid, 
FROM Pslist() 
WHERE Name =~ processRegex

第二步,通过yara规则匹配内存

LET hits = SELECT * FROM foreach(
	row=processes,
	query={
		SELECT ProcessName, CommandLine, Pid, Strings.Offset as Offsets
		FROM proc_yara(rules=yaraRule, pid=Pid)
	}
)

第三步,通过第二步得到的信息,对proc作转储到服务器

SELECT * FROM foreach(
	rows=hits,
	query={
			SELECT ProcessName,CommandLine,Pid,Offsets,FullPath,
			upload(file=FullPath) as CrashDump
			From proc_dump(pid=Pid)
		})

#3 攻击面分析扩展

https://attack.mitre.org

https://github.com/redcanaryco/atomic-red-team

ATT&CK框架的示例,通过模拟攻击,对相关影响资源进行本地监测,然后自定义工件

Elastic

Extending

调用pS

LET Script="dir /"
SELECT * FROM execve(argv="powershell","-executionpolicy", "unrestricted","-c",Script])

#1 服务器配置(VPS Centos)

进入引导配置模式,

velociraptor config generate -i

选择自签名,

然后DNS处输入IP,

What is the public DNS name of the Master Frontend (e.g. [www.example.com](<http://www.example.com/>)): YOUR IP

其余看自己情况或者默认

然后修改server配置文件中的GUI:bind_address为0.0.0.0(不然非本地访问不到)

然后Velociraptor使用指定的linux二进制文件创建服务器Debian软件包。该软件包将包含Velociraptor可执行文件、服务器配置文件和相关启动脚本。

./velociraptor-v0.6.7-5-linux-amd64 --config server.config.yaml rpm server --binary velociraptor-v0.6.7-5-linux-amd64

(如果是debian,请使用官方说明生成deb包)然后安装rpm包

yum install velociraptor_0.6.7-5_server.rpm

systemctl status velociraptor_server

注意rpm name:velociraptor-server.x86_64

补充:在修改配置文件的时候需要重新生成服务器

#2 客户端配置 MAC

service install指令可用于在Mac客户端上安装Velociraptor。以下命令通过launchd安装二进制和配置到/usr/local/sbin.Persistence(使用ps -eaf | grep velosudo launchctl list | grep velo检查)

# velociraptor --config client.config.yaml service install

可以使用以下命令卸载该服务:

# /usr/local/sbin/velociraptor service remove --config=/usr/local/sbin/velociraptor.config.yaml

使用ps -eaf | grep velosudo launchctl list | grep velo确认。

卸载如下

/usr/local/sbin/velociraptor service remove --config=/usr/local/sbin/velociraptor.config.yaml

停止服务如下:

macos守护进程目录如下

sudo launchctl unload -w /Library/LaunchDaemons/com.velocidex.velociraptor.plist

(但是目前只能采集到系统数据,文件采集权限似乎被macos阻止了,后续会更新解决办法)


间桐桜のお菓子屋さん