原文地址:https://zero.lol/2019-08-11-the-year-of-linux-on-the-desktop/
一直以来,关于KDE KConfig漏洞存在很多争议,我决定公开这个问题(完全公开)。 有些人甚至决定写博客来分析这个漏洞,尽管我提供了非常详细的poc。
这就是为什么在这篇文章中,我将详细讲述如何发现漏洞、什么导致我发现了这个漏洞,以及整个研究过程中的思考过程。
首先,总结一下:低于5.61.0的KDE Frameworks (kf5/kdelibs)易受到KConfig类中命令注入漏洞的攻击。该漏洞的利用可通过让远程用户查看特殊构造的配置文件来实现。唯一需要的交互就是在文件浏览器或者桌面上查看文件。当然了,这需要用户下载一个文件,但是隐藏该文件一点都不难。
Bleepingcomputer上传的利用demo
在发布完最后几个EA Origin漏洞后,我很想回到Linux,关注Linux发行版特有的漏洞。我发现Origin客户端是使用Qt框架编写的,而KDE也同样使用Qt框架编写,所以我想要尝试研究一下这个问题。进而,我去研究了KDE。
整个过程中的另一个重要因素是,我一直在自己的笔记本电脑上使用KDE ,对它足够熟悉可以很容易地绘制出攻击面。
当时所做的大部分研究都是和我的一个好朋友分享的,他此前曾经帮助我解决了其他漏洞。谢天谢地,这使我可以轻松地和大家分享我的思考过程。
由于我正在研究KDE,所以我决定先看看他们的默认图片浏览器(gwenview)。背后的想法是,“如果我可以在默认图片浏览器中发现漏洞,那应该是一个相对可靠的漏洞。” 当然,如果我们可以将payload放在图片中,在有人查看或打开图片时触发,那么事情就变得容易多了。
当我意识到gwenview实际上会编译最近查看过的文件列表,并使用KConfig配置语法设置条目时,第一个灵光一现的时刻到了。
此刻我面临的是shell变量。这些变量的解释方式可能决定了我们能否实现命令执行。很明显,在File1
中,它调用$HOME/Pictures/kdelol.gif
并解析变量,否则gwenview如何确定文件位置呢?
为了确认这些配置条目是否真正解释了shell变量/命令,我在Name2中加了些自己的输入。
在看完gwenview后发现。。。发现没什么不同?好吧,这很糟糕,所以我回到了配置文件看看是否有什么变化。结果是,gwenview在启动时会解析shell变量,因此为了解析最近文件,gwenview必须在配置文件更新后重新启动。
一旦发生这种情况,命令将会执行。
正如你所看到的,Name2
中的命令被解析,并解析了$(whoami)
的输出。恢复为Name1
的原因是因为我使用File
复制了条目。这对我们目前来说还没有太大的影响,只要命令在执行,就足以使我们前进。
最初,我并不知道$e是什么意思,所以我进行了必要的挖掘,找到了KDE系统配置文件
文档。
原来$e是用来告诉KDE允许shell扩展的。
在这一点上,这根本不是一个漏洞或一个很突出的问题,不过这看起来确实很危险,我相信可以采取更多措施来滥用它。在发现KDE允许在其配置文件中进行shell扩展后,我向好朋友发了一条信息,详细说明了我所发现的东西。
这里我提出了一个想法,也许可以通过文件名实现内容注入类型的payload。不幸的是,我这样尝试了,KDE似乎可以正确解析新条目并通过增加一个额外的$
来进行转义。无论哪种方式,如果你向某人发送了带有payload的文件,都显然很可疑。有点违背了目的。
这一点上,我不确定应该如何利用这个问题,显然肯定存在某种方法,但这似乎是个坏主意。考虑到这一点,我厌倦了再次尝试相同的事情、阅读相同的文档,所以我休息了一段时间。
最终我回到了KDE,浏览目录,在那里我需要看到隐藏文件(dotfiles)。我转到“控制>显示隐藏文件”,突然发现它在当前工作目录中创建了一个.directory文件。
好吧,很有趣。因为不确定.directory文件是什么,我查看了内容。
[Dolphin]
Timestamp=2019,8,11,23,42,5
Version=4
[Settings]
HiddenFilesShown=true
我注意到的第一件事是,它似乎与KDE对所有配置文件使用的语法一致。我立刻想到,这些条目是否可以被注入shell命令,因为目录打开时KConfig正在读取和处理.directory文件。
我尝试使用shell命令注入version配置项,但是它一直被覆盖,好像行不通。
现在我在想“嗯,也许KDE有一些现有的.directory文件,可以告诉我一些信息”。所以我找到了他们。
zero@pwn$ locate *.directory
/usr/share/desktop-directories/kf5-development-translation.directory
/usr/share/desktop-directories/kf5-development-webdevelopment.directory
/usr/share/desktop-directories/kf5-development.directory
/usr/share/desktop-directories/kf5-editors.directory
/usr/share/desktop-directories/kf5-edu-languages.directory
/usr/share/desktop-directories/kf5-edu-mathematics.directory
/usr/share/desktop-directories/kf5-edu-miscellaneous.directory
[...]
举个例子,我们看下kf5-development-translation.directory
的内容。
kf5-development-translation.directory:
[Desktop Entry]
Type=Directory
Name=Translation
Name[af]=Vertaling
[...]
Icon=applications-development-translation
我注意到在[Desktop Entry]标签下,某些具有keys的条目被调用。例如,在name条目上的af键:
Name[af]=Vertaling
既然KConfig确实在检查条目中的keys,让我们尝试使用$e选项添加keys,就像上述配置文件一样。
在这一点上,我真正感兴趣的另一件事是Icon条目。这里你可以选择设置当前目录或文件本身的图标。如果文件名为.directory,它将为其所在目录设置属性。如果文件名为payload.directory,那么只有payload.directory文件有图标,而不是父目录。为什么会是这样?我们马上讨论这点。
这真的很吸引人,因为这意味着即使不打开文件也可以调用我们的Icon条目,只需要导航到某个目录即可调用。如果在这里使用$e注入命令...该死,那有点太简单了,是不是?
当然,你已经知道了使用下面这个payload的结果了:
payload.directory
[Desktop Entry]
Type=Directory
Icon[$e]=$(echo${IFS}0>~/Desktop/zero.lol&)
演示视频:https://www.youtube.com/watch?v=l4z7EOQQs84
跟任何漏洞一样,访问代码可以使我们的生活变得轻松。充分理解我们的“利用方式”对最大限度发挥影响和写出高质量报告十分重要。
目前,我已经确定了几件事情:
1)问题实际上是KDE配置的一个设计缺陷
2)只需查看文件/文件夹即可触发
问题本身显然在KConfig中,但是如果我们无法调用配置文件...也就没有办法触发它。所以这里有几个部分。带着这些信息,我决定看看KConfig和KConfigGroup的代码。这里我找到了一个名为readEntry()的函数。
kconfiggroup.cpp
我们可以看到它在做的一些事情:
1)检查条目的key
2)如果$e这个key存在,expandString()
会读取它的值。
显然现在我们需要了解expandString()
的作用。通过搜索文件,我们在kconfig.cpp中找到了这个函数。
kconfig.cpp
长话短说:
1)检查$
字符;
2)检查是否后面有()
;
3)调用popen传入该值
4)返回值(必须去掉该部分)
这基本上解释了它的大部分工作原理,但是我想按照代码准确找到在哪里readEntry()
和expandString()
被调用,然后执行我们的命令。
在github上搜索了很长一段时间后,我确定有一个特定于桌面文件的函数,叫做readIcon()
,位于KDesktopFile类中。
kdesktopfile.cpp
基本上它仅用了readEntry()
函数,在配置文件中获取Icon。了解到这个函数存在...我们可以回到源代码,搜索readIcon()
。
目前为止,我一直只是在研究.directory文件,但是在阅读更多代码后,发现KDesktopFile类不仅仅用于.directory文件。它也可以用于.desktop文件(谁能想到呢?????)。
因为KDE将.directory和.desktop看作KDesktopFile的文件,并且icon在这个类中调用(或许其他类,在这里并不重要),所以如果我们注入命令,那么命令将会被执行。
我们知道如果可以让某人查看.directory或.desktop文件,readEntry()
将会被调用,从而执行我们的代码。我认为肯定有更多触发readEntry的方法。理想情况下,是完全远程的、少交互,即不用下载文件。
解决这个问题的方法是,在iframe中使用smb:// URI来提供用户将要连接的远程共享,最终在他们连接时执行我们的directory文件。
不幸的是,KDE不同于GNOME ,它不会自动挂载远程共享,如果文件系统上不存在.directory/.desktop,则不信任他们。
这基本上破坏了让用户意外浏览到远程共享并执行任意代码的目的。很有趣,因为自动挂载远程共享是KDE用户很久以来一直要求的功能特性。如果实现了这点,这个攻击可能会更加危险。
无论如何,我们不能自动挂载远程共享,但是KDE确实有一个客户端,用于方便使用KDE用户普遍使用的SMB共享。这个应用程序叫做SMB4k,实际上没有与KDE一起提供。
使用SMB4k挂载共享后,就可以通过Dolphin进行访问。
如果我们对公共SMB共享可写,(人们正在使用SMB4k浏览)我们就可以植入恶意配置文件,当在Dolphin中查看该文件时,它将会显示如下内容,最终实现了远程代码执行。
向某人发送.directory或.desktop文件显然会引发很多问题,对吗?我想是的。这也是大多数关于这个话题的评论所说的。为什么这不重要?因为嵌套文件和伪造文件扩展名是你可能想到的最简单的事情。
这里我们可以作出选择。第一个选择是创建一个嵌套目录,在打开父目录后立刻加载图标,甚至可以在没有看到目录或不知道目录内容的情况下执行代码。例如,查看Apache网站上下载的httpd。
毫无戒心的用户不可能看出其中某个目录嵌套了一个恶意的.directory文件。如果你期盼出现,可以,但通常来讲,不会有任何怀疑。
nested directory payload
$ mkdir httpd-2.4.39
$ cd httpd-2.4.39
$ mkdir test; cd test
$ vi .directory
[Desktop Entry]
Type=Directory
Icon[$e]=$(echo${IFS}0>~/Desktop/zer0.lol&)
压缩文件,发送出去。
在文件管理器中打开httpd-2.4.39文件夹的时候,test目录将会试图加载Icon,从而执行命令。
我们的第二个选择是,“伪造”文件扩展名。实际上我忘记在最初的poc中记录这种方法,这就是我为什么现在将其包括在这里。事实上,当KDE不能识别文件扩展名时,它会试图变“智能化”,分配一个文档类型。如果文件开头包含[Desktop Entry],该文件会被分配到application/x-desktop类型。最终允许文件在加载时由KConfig处理。
在此基础上,我们可以用一个类似于“t”的字符制作一个假的TXT文件。为了演示隐藏文件十分简单,我再次使用了httpd包。
很明显图标会暴露文件,但是这种方法仍然比比随机的.directory/.desktop文件谨慎的多。
同样的,只要文件夹一打开,代码就会被执行。
坦白来说,这个方法相对没用,但是我认为在演示中它会很酷,同时在payload的传递中添加一些社会工程学元素。
当我分析KDE时,我(偶然)意识到,你实际上可以拖放远程资源,并且拥有一个文件传输触发器。这些都由KIO (kde 输入/输出模块)启用。
这基本上允许用户拖放远程文件,并传输到本地文件系统中。
实际上,如果我们可以让用户拖放链接,文件传输将会触发并最终在文件加载到系统时执行任意代码。
多亏了KDE团队,只要打了必要的补丁,您就不必再担心这个漏洞。
非常感谢他们在得知此问题约24小时内就对此发布了补丁,是令人印象深刻的响应。
我还要在此对以下的朋友表示感谢,他们在整个过程中给予我很大帮助。请查看参考文献中Nux分享的payload :)
· Nux
· yuu