Formula Injection-公式注入

近期在twitter上有安全研究者分享了一个针对excel公式的命令执行payload,在本地验证成功,于是查阅了一些资料并做了一些粗浅的分析。

背景

针对excel公式解析的命令执行问题最早能追溯到2013年的一篇文章[1],在该文中这种攻击方式称为Cell Injection。2014年,又有研究者发表了类似攻击的文章[2]并公布了对应的CVE-2014-3524:

CVE-2014-3524 OpenOffice Calc Command Injection Vulnerability

Apache OpenOffice 4.1.0 and older on Windows.

OpenOffice.org versions are also affected

The vulnerability allows command injection when loading Calc spreadsheets. Specially crafted documents can be used for command-injection attacks. Further exploits are possible but have not been verified.

CVE-2014-3524涉及两大开源office套件,Apache OpenOffice和LibreOffice。其中Apache OpenOffice于4.1.1版本修复该问题,LibreOffice于4.2.6-secfix(4.2.6.3) /4.1.3版本修复该问题。使用范围最广的微软office也受该CVE影响,但微软并没有承认这个CVE,也没有如Apache OpenOffice和LibreOffice完全移除相关功能,而是通过给出安全提示并在2017年底的补丁中新增了可禁用相关功能的配置项不完全地修复了这个问题。对于该漏洞,后文将统称为公式注入。

总体而言,公式注入是针对客户端的攻击,类似XSS,需要客户端的主动操作触发攻击。常见的攻击方式为通过WEB前端页面输入恶意数据,管理员从管理后台通过“导出到csv”“导出到xls”等功能导出数据到csv、xls或xlsx等表格文件时,导出的文件将包含恶意代码,通过office excel等办公软件打开表格文件时会自动解析其中的公式,从而执行恶意代码。对于这类攻击,office办公软件会给出相应的安全提示,但基于以下两种假设,管理员很可能会忽略安全提示。

1 认为csv属于纯文本不可执行,但实际上打开csv的office软件会将“纯文本”解析为代码并执行

2 认为office软件常见的攻击是基于宏,而该提示并未涉及宏

3 认为文件源于可信环境,但实际上文件中的数据源于不可信环境

最原始的攻击方式

公式注入最早的攻击方式利用excel原生的公式和函数进行攻击,如HYPERLINK(link_location,friendly_name),通过前台表单输入payload,后台导出并查看数据时,诱使受害者点击超链接访问恶意网站,可以偷取数据或是通过下载恶意软件和浏览器漏洞getshell。

诱骗点击超链接:

通过将单元格的引用添加到http参数中实现信息泄露:

服务端监听到的http请求:

通过将恶意url与函数分开的方式隐藏url并逃避双引号过滤:

这种古老的攻击方式比较鸡肋,需要主动点击单元格才能触发,但是当前版本的office不会有任何安全提示。

命令执行原理

为了达到命令执行的目的,需要通过动态数据交换DDE (Dynamic Data Exchange)这一协议。这是一种“在Microsoft Windows或OS/2操作系统中运作进程间通信的技术。DDE主要传递的数据流通常是不需要用户经常干涉的。DDE可以允许Windows应用程序共享数据,例如,Microsoft Excel(电子表格)中的单元格在另一个挂载的应用程序中的数值发生改变时,Excel会自动做出更新”(摘自wiki)。Microsoft Excel,LibreOffice和OpenOffice均支持。这是微软操作系统自带的一种合法的数据交换的协议,只不过office套件在实现该协议时画蛇添足地过度实现导致了命令执行。DDE协议通过一个三元组application,topic,item表示,其中application代表目标进程,topic代表该进程内的某个数据组,item表示该数据组内的某个数据碎片,例如从某个excel文件内请求数据,则excel.exe表示application,XXX.xls表示topic,单元格位置表示item。那为什么通过DDE能执行命令呢?

首先看一下开源的OpenOffice的实现方式:

OpenOffice内置了DDE这么一个函数,正常的调用方式为通过该函数引用另一个表格内的数据,如:

=DDE("soffice";"C:\OpenOffice\test1.ods";"Sheet1.C7")

但是利用该函数的特性就可以实现恶意调用:

=DDE("cmd";"/C calc";"123")

通过阅读4.1.0版本openoffice/main/sfx2/source/appl/impldde.cxx的源码发现,最终调用的Connect函数中如果第一个参数server无法访问时,将会在server后添加.exe后缀并和file拼接形成命令,调用WinExec api执行(原注释是德文,所以截图里是机翻):