开源osquery扩展库发布:EFIgy固件安全检测技术解析

本文详细介绍Trail of Bits开源的osquery扩展库,重点解析如何通过EFIgy API检测Mac设备EFI固件安全性,包含完整的REST API调用示例和osquery扩展开发技术指南。

Trail of Bits osquery扩展库发布公告

今天,我们正式开放维护的osquery扩展代码库。首个扩展利用Duo Labs的EFIgy API来检测Mac设备群的EFI固件是否处于最新状态。

现状与挑战

目前公开的osquery扩展示例极少,相关文档也十分匮乏。本文旨在帮助开发者了解为osquery编写扩展的完整流程,重点介绍我们实现EFIgy扩展的具体方法。

EFIgy技术背景

在今年的Ekoparty安全会议上,Duo Labs展示了关于EFI固件支持状况和安全性的研究成果。EFI固件因其运行权限甚至超过操作系统和虚拟机监控程序而成为攻击者的重点目标。Duo Labs收集分析了过去三年所有公开的Apple更新,并通过检查73,000多台Mac设备验证了这些信息。

研究发现许多计算机运行着过时的固件,即使所需的EFI更新本应包含在主机已正确安装的操作系统补丁中。为此Duo Labs创建了EFIgy服务——一个REST端点,可通过逻辑板ID和产品名称等详细信息访问任何已知Apple产品的最新OS和EFI版本。

编程查询EFIgy API

EFIgy要求传入包含待查询系统详情的JSON对象,所需字段主要包括硬件型号和软件版本:

1
2
3
4
5
6
7
8
9
{
  "board_id": "Mac-66E35819EE2D0D05",
  "smc_ver": "2.37f21",
  "build_num": "16G07",
  "rom_ver": "MBP132.0226.B20",
  "hw_ver": "MacBookPro12,1",
  "os_ver": "10.12.4",
  "hashed_uuid": ""
}

其中rom_ver字段需要注意:它不包含完整的EFI版本(因为包含的时间戳信息实用价值有限),只需关注点分隔后的第一、第三和第四部分。

hashed_uuid字段是SHA256摘要,计算方式是将主网络接口的MAC地址前缀添加到系统UUID前:“0x001122334455” + “12345678-1234-1234-1234-1234567890AB”。

其他字段含义明确,但需注意在虚拟机环境中运行时可能无法正确报告。board_idhw_verrom_ver将报告虚拟机监控程序提供的虚拟组件信息。

通过HTTP POST请求将JSON数据发送到REST端点即可查询服务:

1
https://api.efigy.io/apple/oneshot

服务器响应包含三个JSON对象,与原始值比较即可判断系统是否最新:

1
2
3
4
5
{
  "latest_efi_version": {"msg": "<version>"},
  "latest_os_version": {"msg": "<version>"},
  "latest_build_number": {"msg": "<error message>", "error": "1"}
}

osquery扩展开发实践

虽然Duo Labs的工具使用简便,但在每个系统上手动运行并不现实。我们决定实现一个查询EFIgy的osquery扩展。

选择编写扩展而非虚拟表的原因:我们将原生操作系统功能保留在核心中,其他所有功能都转换为扩展。如果新功能使用外部服务或非原生组件,默认编写扩展。

开发环境仅限标准库和手动导入的项目依赖。osquery静态链接所有内容,需要特别注意使用的库,不能依赖运行时加载动态库。

表扩展的实现相对简单,只需继承osquery::TablePlugin类并重写定义列和生成表行的方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class MyTable final : public osquery::TablePlugin {
private:
    osquery::TableColumns columns() const override {
        return {std::make_tuple("column_name", 
                               osquery::TEXT_TYPE,
                               osquery::ColumnOptions::DEFAULT)};
    }

    osquery::QueryData generate(osquery::QueryContext& request) override {
        osquery::Row row;
        row["column_name"] = "value";
        return {row};
    }
};

源文件必须放置在osquery/external下的专用文件夹中,且文件夹名称必须添加"extension_“前缀,否则CMake项目将忽略它。

对于复杂项目,可以添加CMakeLists.txt文件,使用以下辅助函数创建目标:

1
ADD_OSQUERY_EXTENSION(${PROJECT_NAME} source1.cpp source2.cpp)

虽然可以访问osquery中的某些库(如Boost),但无法使用其他实用程序(如非常有用的http_client类)。

虽然没有官方的扩展开发步骤推荐,但如果计划编写多个扩展,建议将实用函数打包到头文件中以便导入和重用。保持所有外部库静态链接也是个好主意,这将简化重新分发过程。

使用我们的osquery扩展库

由于没有提交新功能的地方,我们创建了新的扩展存储库。EFIgy扩展是首个可用项目,后续将添加更多内容。

使用存储库很简单,但需要先克隆osquery的完整源代码,因为SDK不包含在可分发包中。构建扩展很容易,只需在osquery/external文件夹中创建要编译的源文件夹的符号链接,并按照extension_<name>的命名规则命名链接,然后按照常规构建流程进行:

1
2
3
4
5
6
7
8
cd /src/osquery-extensions
ln -s efigy /src/osquery/external/extension_efigy

cd /src/osquery
make sysprep
make deps
make -j `nproc`
make externals

扩展使用简单,可通过--extension参数指定路径进行测试(包括shell和守护进程)。由于是正常可执行文件,也可以在osquery启动后启动它们,它们将通过Thrift自动连接并暴露新功能。官方文档对此过程有详细说明。

快速测试扩展的方法:

1
osqueryi --extension /path/to/extension

行动建议

如果您拥有Mac设备群,现在可以通过osquery和EFIgy扩展进行监控,确保所有端点都收到了所需的软件和固件更新。

如果您在未来某个时间阅读本文,更有理由访问我们的osquery扩展存储库。我们将持续维护并添加新内容。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计