FwAnalyzer是一个使用一组可配置规则分析(ext2/3/4),FAT/VFat,SquashFS,UBIFS文件系统镜像和目录内容的工具。FwAnalyzer依赖于e2tools用于ext文件系统,mtools用于FAT文件系统,squashfs-tools用于SquashFs文件系统,ubi_reader用于UBIFS文件系统。对ext2/3/4镜像的SELinux/xattr支持需要修补版本的e2tools。
FwAnalyzer旨在提供一种快速分析文件系统镜像的工具。FwAnalyzer有一个专门的配置文件,该文件定义了文件和目录的各种规则,并针对给定的文件系统镜像运行已配置的检查。FwAnalyzer的输出是一个报告,其中包含违反配置中指定的任何规则的文件列表。该报告还包含有关文件系统镜像的元信息,以及从分析的文件系统中提取的信息(如果已配置)。报告使用JSON格式,因此可以轻松的将其集成到大型的分析步骤当中。
报告示例:
{
"fs_type": "extfs",
"image_digest": "9d5fd9acc98421b46976f283175cc438cf549bb0607a1bca6e881d3e7f323794",
"image_name": "test/test.img",
"current_file_tree_path": "test/oldtree.json.new",
"old_file_tree_path": "test/oldtree.json",
"data": {
"Version": "1.2.3",
"date1 file": "Mon Oct 1 16:13:05 EDT 2018\n"
},
"informational": {
"/bin": [
"CheckFileTree: new file: 40755 1001:1001 1024 0 SeLinux label: -"
],
},
"offenders": {
"/bin/elf_arm32": [
"script(check_file_elf_stripped.sh) returned=elf_arm32 is not stripped"
],
"/file1": [
"File not allowed"
],
"/file2": [
"File is WorldWriteable, not allowed",
"File Uid not allowed, Uid = 123"
],
}
}
按照构建中描述的步骤安装所有所需项并构建FwAnalyzer。
命令行选项
cfg:string,配置文件的路径
cfgpath:string,配置文件的路径和包含的文件(可以重复)
in:string,文件系统镜像文件或目录路径
out:string,使用’-'将报告输出到文件或标准输出
tree:string,覆盖目录以从中读取filetree文件
ee:如果存在违规,则error退出
invertMatch:反转(invert )正则表达式匹配(用于测试)
示例:
fwanalyzer -cfg system_fwa.toml -in system.img -out system_check_output.json
使用存储在scripts目录中的自定义脚本的示例:
PATH=$PATH:./scripts fwanalyzer -cfg system_fwa.toml -in system.img -out system_check_output.json
devices文件夹包含用于解包和处理特定设备类型和固件包格式(如Android)的helper脚本。它还包括可以包含在特定于目标的FwAnalyzer配置中的常规配置文件。
scripts文件夹包含可从FwAnalyzer调用的helper脚本,用于文件内容分析和数据提取。
全局配置用于定义一些常规参数。
FsType(文件系统类型)字段选择用于访问镜像中文件的后端。FsType支持的选项有:
dirfs:从运行fwanalyzer主机上的目录中读取文件(支持的FsTypeOptions:N/A)
extfs:读取ext2/3/4文件系统镜像(支持的FsTypeOptions:selinux)
squashfs:读取SquashFS文件系统镜像(支持的FsTypeOptions:N/A)
ubifs:读取UBIFS文件系统镜像(支持的FsTypeOptions:N/A)
vfatfs:读取VFat文件系统镜像(支持的FsTypeOptions:N/A)
FsTypeOptions允许调整FsTyp驱动程序。
DigestImage选项将生成已分析的文件系统镜像的SHA-256摘要,该摘要将包含在输出中。
示例:
[GlobalConfig]
FsType = "extfs"
FsTypeOptions = "selinux"
DigestImage = true
输出示例:
"fs_type": "extfs",
"image_digest": "9d5fd9acc98421b46976f283175cc438cf549bb0607a1bca6e881d3e7f323794",
"image_name": "test/test.img",
Include语句用于将其他FwAnalyzer配置文件包含到包含该语句的配置中。include语句可以出现在配置的任何部分。-cfgpath参数设置包含文件的搜索路径。
示例:
[Include."fw_base.toml"]
GlobalFileChecks是应用于整个文件系统的更通用的检查。
Suid:bool,(可选)启用后,如果任何文件设置了粘滞位,分析将失败(默认值:false)
SuidWhiteList:string array,(可选)允许Suid检查的白名单文件(按完整路径)
WorldWrite:bool,(可选)启用后,如果任何用户都可以写入任何文件,则分析将失败(默认值:false)
SELinuxLabel:string,(可选)启用后,如果文件没有SeLinux标签,分析将失败
Uids:int array,(可选)指定系统中允许的每个UID,每个文件都需要由此列表中指定的Uid所有
Gids:int array,(可选)指定系统中允许的每个GID,每个文件都需要由此列表中指定的Gid所有
BadFiles:string array,(可选)指定不需要的文件列表,允许使用通配符,如?,* 和 **(此列表中不应存在任何文件)
BadFilesInformationalOnly:bool,(可选)BadFile检查的结果将仅为信息(默认值:false)
示例:
[GlobalFileChecks]
Suid = true
SuidWhiteList = ["/bin/sudo"]
SELinuxLabel = false
WorldWrite = true
Uids = [0,1001,1002]
Gids = [0,1001,1002]
BadFiles = ["/file99", "/file1", "*.h"]
输出示例:
"offenders": {
"/bin/su": [ "File is SUID, not allowed" ],
"/file1": [ "File Uid not allowed, Uid = 123" ],
"/world": [ "File is WorldWriteable, not allowed" ],
}
FileStatCheck可用于为特定文件或目录建模元数据。任何配置的变化都将被报告为违规(offender)。
AllowEmpty:bool,(可选)定义文件的大小可以为0(默认值:false)
Uid:int,(可选)指定文件的UID,不指定UID或指定-1将跳过检查
Gid:int,(可选)指定文件的GID,不指定GID或指定-1将跳过检查
Mode:string,(可选)以八进制指定UN * X文件模式/权限,不指定模式将跳过检查
SELinuxLabel:string,(可选)文件的SELinux标签(如果没有设置,将跳过检查)
LinkTarget:string,(可选)符号链接的目标,未指定链接目标将跳过检查。目前仅dirfs,squashfs和ubifs文件系统支持此功能。
Desc:string,(可选)是一个描述性字符串,如果检查失败,将附加到报告中
InformationalOnly:bool,(可选)检查结果将仅供参考(默认值:false)
示例:
[FileStatCheck."/etc/passwd"]
AllowEmpty = false
Uid = 0
Gid = 0
Mode = "0644"
Desc = "this need to be this way"
输出示例:
"offenders": {
"/file2": [ "File State Check failed: size: 0 AllowEmpyt=false : this needs to be this way" ],
}
FilePathOwner检查可用于为文件系统的整个tree建模文件/目录所有权。如果给定目录中的任何文件或目录不归指定的Uid和Gid(type:int)所有,则检查失败。
示例:
[FilePathOwner."/bin"]
Uid = 0
Gid = 0
输出示例:
"offenders": {
"/dir1/file3": [ "FilePathOwner Uid not allowed, Uid = 1002 should be = 0",
"FilePathOwner Gid not allowed, Gid = 1002 should be = 0" ],
}
FileContent检查允许检查文件的内容。可以使用四种不同的方法检查文件的内容。通过将InformationalOnly设置为true(默认为false),可以在非强制模式下运行文件内容检查。InformationalOnly检查将产生信息元素替代违规。
示例:整个文件体上的正则表达式
File:string,文件的完整路径
RegEx:string,posix/golang正则表达式
RegExLineByLine:bool,(可选)逐行应用正则表达式,匹配行将在结果中(默认值:false)
匹配:bool,(可选)指示正则表达式匹配或是不匹配(默认值:false)
Desc:string,(可选)是一个描述性字符串,将附加到失败的检查
InformationalOnly:bool,(可选)检查结果将仅供参考(默认值:false)
示例:
[FileContent."RegExTest1"]
RegEx = ".*Ver=1337.*"
Match = true
File = "/etc/version"
示例:通过文件体计算的SHA-256摘要
File:string,文件的完整路径
Digest:string,HEX编码摘要
Desc:string,(可选)是一个描述性字符串,将附加到失败的检查
InformationalOnly:bool,(可选)检查结果仅供参考
示例:
[FileContent."DigestTest1"]
Digest = "8b15095ed1af38d5e383af1c4eadc5ae73cab03964142eb54cb0477ccd6a8dd4"
File = "/ver"
输出示例:
"offenders": {
"/ver": [ "Digest (sha256) did not match found = 44c77e41961f354f515e4081b12619fdb15829660acaa5d7438c66fc3d326df3 should be = 8b15095ed1af38d5e383af1c4eadc5ae73cab03964142eb54cb0477ccd6a8dd4." ],
}
示例:运行一个外部脚本传递文件名到脚本
在执行脚本之前,将文件解压缩到具有临时名称的临时目录中。如果脚本在stdout或stderr上生成输出,则检查会产生违规。
File:string,文件或目录的完整路径
Script: string,脚本的完整路径
ScriptOptions:string array,(可选)第一个元素允许定义包含通配符的模式,如?,* 和 ** 应用于文件名(如果存在)它只会检查与模式匹配的文件,这在目录上运行脚本时非常有用。第二个元素允许传递参数到脚本。
File:string,文件的完整路径,如果路径指向目录,则为目录和子目录中的每个文件运行脚本
Desc:string,(可选)是一个描述性字符串,将附加到失败的检查
InformationalOnly:bool,(可选)检查结果将仅供参考(默认值:false)
如果–存在,则表示下一个参数来自ScriptOptions[1]。该脚本使用以下参数运行:
<tmp filename> <original filename> <uid> <gid> <mode in octal> <selinux label or "-" for no label> [--] [script options argument]
示例:
[FileContent."ScriptTest1"]
Script = "check_file_x8664.sh"
File = "/bin"
输出示例:
"offenders": {
"/bin/elf_arm32": [ "script(check_file_x8664.sh) returned=elf_arm32 not a x86-64 elf file" ],
}
File:string,文件的完整路径
Json:string,字段名称,使用点(.)表示法访问对象内的字段,冒号(:)分隔所需的值。所有类型都将转换为字符串并作为字符串进行比较。Json数组可以通过提供索引而不是字段名来进行索引。
Desc:string,(可选)是一个描述性字符串,将附加到失败的检查
InformationalOnly:bool,(可选)检查结果将仅供参考(默认值:false)
示例:
[FileContent."System_Arch"]
Json = "System.Arch:arm64"
File = "/system.json"
Desc = "arch test"
输出示例:
{
"System": {
"Version": 7,
"Arch": "arm32",
"Info": "customized"
}
}
Example Output:
```json
"offenders": {
"/system.json": [ "Json field System.Arch = arm32 did not match = arm64, System.Arch, arch test" ],
}
FileTree检查生成完整的文件系统树(每个文件和目录的列表),并将其与先前保存的文件树进行比较。该检查将生成一个信息输出,列出新文件,已删除文件和已修改文件。
CheckPath(string array)指定应包含在检查中的路径。如果未设置CheckPath,它将设置为[“/”]并将包含整个文件系统。如果CheckPath设置为[],它将生成文件树,但不会检查任何文件。
OldFileTreePath指定从旧filetree读取的文件名,如果生成了新的filetree(例如因为旧文件树不存在),则新生成的filetree文件为OldFileTreePath,并添加“.new”后缀。
OldFileTreePath是相对于配置文件的。这意味着’-cfg testdir/test.toml’与 OldTreeFilePath = “test.json将尝试读取’testdir/test.json’。-tree命令行选项可用于覆盖路径:’-cfg testdir/test.toml -tree test1′将尝试读取’test1/test.json’。类似地,新生成的filetree文件将存储在同一目录中。
文件修改检查可使用以下参数自定义:
CheckPermsOwnerChange:如果更改了所有者或权限(模式),bool,(可选)会将文件标记为已修改(默认值:false)
CheckFileSize:bool,(可选)将标记文件,因为修改后的大小已更改(默认值:false)
CheckFileDigest:bool,(可选)会在内容发生变化时将文件标记为已修改(比较它的SHA-256摘要)(默认值:false)
SkipFileDigest:bool,(可选)跳过计算文件摘要(用于处理非常大的文件,默认为:false)
示例:
[FileTreeCheck]
OldTreeFilePath = "testtree.json"
CheckPath = [ "/etc", "/bin" ]
CheckPermsOwnerChange = true
CheckFileSize = true
CheckFileDigest = false
输出示例:
"informational": {
"/bin/bla": [ "CheckFileTree: new file: 40755 1001:1001 1024 0 SeLinux label: -" ]
}
DirCheck(目录内容)检查在指定目录中指定一组允许或需要检查的文件。在该目录中找到的任何其他文件或目录都将被报告为违规。如果未找到Allowed文件,则检查将通过。如果找不到Required文件,则会将其报告为违规。
文件项可以包含通配符,如?,*和**。allowed模式已在golang文档中描述。
每个目录只能存在一个DirCheck项。
示例:
[DirContent."/home"]
Allowed = ["collin", "jon"]
Required = ["chris"]
DataExtract选项允许从文件中提取数据并将其包含在报告中。可以通过正则表达式,运行外部脚本或读取JSON对象来提取数据。提取的数据之后可由后处理脚本使用。
数据提取功能将数据作为key:value对的映射添加到报表中。key被定义为语句的名称或可选的Name参数。该值是正则表达式或脚本输出的结果。
示例:基于正则表达式的数据提取
正则表达式生成的输出将存储为此语句名称的值,下面的示例名为“Version”。
File:string,文件的完整路径
RegEx:string,带有一个匹配字段的正则表达式
Name:string,(可选)键名
Desc:string,(可选)描述
示例:
键“Version”将包含正则表达式的输出。
[DataExtract."Version"]
File = "/etv/versions"
RegEx = ".*Ver=(.+)\n"
Desc = "Ver 1337 test"
输出示例:
"data": {
"Version": "1.2.3",
}
示例:基于脚本的数据提取
脚本生成的输出将存储为此语句名称的值,下面的示例名为LastLine。
File:string,文件的完整路径
Script:string,脚本的完整路径
Name:string,(可选)键名
Desc:string,(可选)描述
该脚本使用以下参数运行:
<tmp filename> <original filename> <uid> <gid> <mode in octal> <selinux label or "-" for no label>
示例:
键“script_test”将包含脚本的输出。该语句的名称为“scripttest”
[DataExtract.scripttest]
File = "/etc/somefile"
Script = "extractscripttest.sh"
Name = "script_test"
输出示例:
"data": {
"script_test": "some data",
}
示例:JSON数据提取
脚本生成的输出将存储为此语句名称的值,下面的示例名为LastLine。
File:string,文件的完整路径
Json:string,使用点(.)表示法访问对象中字段的字段名称
Name:string,(可选)键名
Desc:string,(可选)描述
示例:
键“ROS_Info”将包含来自/etc/os_version.json下System对象的Info字段内容。
{
"System": {
"Version": 7,
"Arch": "arm32",
"Info": "customized"
}
}
[DataExtract.OS_Info]
File = "/etc/os_version.json"
Json = "System.Info"
Name = "OSinfo"
输出示例:
"data": {
"OSinfo": "customized",
}
可以通过提供索引而不是字段名来索引Json数组。
示例:高级用法
DataExtract语句允许具有相同名称(相同键)的多个条目。这对于配置多种提取相同信息的方法非常有用。生成有效输出的第一个数据提取语句将设置给定键的值。这适用于正则表达式和脚本以及两者同时使用。
下面的示例显示了两个语句,它们都将为键“Version”创建键值对。如果“1”没有产生有效输出,则尝试下一个输出,在本例中为“2”。
示例:
[DataExtract."1"]
File = "/etc/versions"
RegEx = ".*Ver=(.+)\n"
Name = "Version"
[DataExtract."2"]
File = "/etc/OSVersion"
RegEx = ".*OS Version: (.+)\n"
Name = "Version"
*参考来源:GitHub,FB小编secist编译,转载请注明来自FreeBuf.COM