导语:本文我会给大家介绍一种技术,通过在Mozilla Firefox中包含一个用于基于智能卡的身份验证(PKCS11)的定制模块,可以在底层操作系统上执行代码。本文主要关注Linux环境,但是当底层操作系统是Microsoft Windows时,本文所讲的操作方法也一
在软件环境中,用户只能执行有限数量的任务,通常使用Kiosk应用程序来减少用户与系统交互的机会。在许多环境中,只有少数预定义的应用程序发布给用户,比如web浏览器或POS软件,这取决于用户需要做什么。
攻击这种受限环境的攻击者通常试图找到一种绕过已发布应用程序边界的方法,以便在底层操作系统上执行命令。攻击者的总体目标通常是在系统上建立一个立足点,再感染与之相连的其他系统。根据被攻击系统的目的不同,操作和财务损失(包括敏感数据的丢失)也可能有所不同。
在Windows中,攻击者有许多众所周知的方法来创建交互式shell,例如粘滞键、“打开文件”对话框、在地址栏输入命令、帮助菜单等。在Linux环境中,这个过程更复杂,不是只在浏览器的地址栏中输入命令即可。
本文我会给大家介绍一种技术,通过在Mozilla Firefox中包含一个用于基于智能卡的身份验证(PKCS11)的定制模块,可以在底层操作系统上执行代码。本文主要关注Linux环境,但是当底层操作系统是Microsoft Windows时,本文所讲的操作方法也一样适用。
目标侦察
在执行安全评估时,第一步便是侦察,以便尽可能多的发现目标系统。如果Firefox是唯一可用的应用程序,那么侦察将更加困难,但并非不可能。“文件打开”对话框有助于确定文件系统的结构。在某些情况下,可以锁定文件打开或文件保存的对话框,使目录和文件都隐藏起来。但是,Firefox允许任何用户通过使用“file:”协议处理程序来遍历文件系统。下面的屏幕截图显示了调用file:///之后底层操作系统的根目录
我们感兴趣的目录的是/etc目录,它可以包含关于系统配置的有价值的信息,或可以全局写入的/ tmp目录,攻击者可以利用这个目录在系统上删除文件。我们不仅可以浏览目录,还可以浏览当前用户可读的/etc/os-release等文件。
收集一些关于操作系统的信息对于后续操作都很重要,通过“帮助”菜单和“关于Firefox”视图进行的进一步搜索,可以发现Firefox是作为一个32位进程运行的。这个信息很有用,因为模块的目标架构必须与流程的架构相匹配。
使用自定义PKCS11模块
生成交互式shell的选项是有限的,但是Mozilla Firefox中的“安全设备”功能允许加载本地存储的* .so或* .dll文件。此功能主要用于设置基于智能卡的身份验证,例如,当web服务器需要客户端证书时。该实现会基于PKCS11标准,该标准还定义了与智能卡通信的API。
现在我们已经确定了要下载文件的架构和位置,创建一个功能PKCS11库需要以下三个步骤,它会生成一个shell并与Mozilla Firefox兼容:
1.找到一个合适的PKCS11库;
2.编译PKCS11库;
3.调整生成的库的动态依赖关系;
找到一个合适的PKCS11库
这样一个模块的基础是开源中间件“OpenSC”,可以在Github上找到。
能够与PKCS11 API实现通信的库需要实现以下功能:
C_Initialize() C_GetInfo() C_GetSlotList() C_GetTokenInfo() C_OpenSession() C_GetMechanismList()
从头实现这样一个模块将非常耗时,但是可以利用现有的OpenSC代码库进行必要的自定义改编。
编译PKCS11库
为了理解代码流,首先找到某种类型的“入口点”是很重要的。特别有趣的是src/pkcs11/pkcs11-global.c文件:
CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { CK_RV rv; #if !defined(_WIN32) pid_t current_pid = getpid(); #endif int rc; unsigned int i; sc_context_param_t ctx_opts;
上面的代码段显示了浏览器加载PKCS11模块后自动调用的函数C_Initialize,对于一些生成的额外的代码行来说,这是一个好的开始。
CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { int res = system("/usr/bin/xterm"); printf("%d", res); CK_RV rv; #if !defined(_WIN32) pid_t current_pid = getpid(); #endif int rc; unsigned int i; sc_context_param_t ctx_opts;
插入了对system()命令的调用,,该命令调用外部应用程序(本文为/usr/bin/xterm)来生成交互式终端。请注意,代码中的“system()”调用会阻止Firefox进程的主线程,并在xterm关闭之前保持停止响应。然而,对于我们的这次概念证明来说已经足够了。
对于简单的概念验证,这种修改就足够了。OpenSC项目现在需要为32位架构进行编译,因为Firefox实例是作为32位进程启动的。编译项目之后,Firefox可以打开的共享库以“opensc-pkcs11”的名称创建,可以在src/pkcs11/.libs/中找到。在可访问的web服务器上托管文件后,需要将其下载到目标系统,例如使用浏览器的“Save link as”功能。将这个文件加载到Firefox中后,通常初始加载会失败,并显示错误消息“模块加载失败”。这与一个opensc-pkcs11依赖的库(不过该库已经删除)有关,且意味着需要调整动态依赖关系。
调整结果库的动态依赖关系
要找出哪些库已经被删除,可以使用“ldd”命令:
[email protected]:~/OpenSC/src/pkcs11/.libs# ldd opensc-pkcs11.so linux-gate.so.1 (0xb774b000) libopensc.so.6 => /root/OpenSC/src/libopensc/.libs/libopensc.so.6 (0xb75a4000) libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb758c000) libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb756d000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb73b6000) /lib/ld-linux.so.2 (0xb774d000)
如上所示,编译后的模块opensc-pkcs11.so依赖于一个名为“libopensc.so.6”的库,存储在/root/OpenSC/src/libopensc/.libs/libopensc.so.6中。到目前为止,只有opensc-pkcs11.so被下载到目标系统,并且库找不到libopensc.so.6,从而导致错误。
工具“patchelf”可用于调整加载libopensc.so.6的位置,要将搜索路径从/root/OpenSC/src/libopensc/.libs/更改为/ tmp /,可以使用以下命令:
patchelf --set-rpath /tmp opensc-pkcs11.so
上面的命令会将“rpath”更改为/tmp,要检查修改是否成功,可以再次调用ldd命令。
[email protected]:~/OpenSC/src/pkcs11/.libs# ldd opensc-pkcs11.so linux-gate.so.1 (0xb774b000) libopensc.so.6 => /tmp/libopensc.so.6 (0xb75a4000) libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb758c000) libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb756d000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb73b6000) /lib/ld-linux.so.2 (0xb774d000)
修改共享对象后,就要准备生成所有有效载荷了。要生成shell,需要执行以下操作:
1. 将修改后的libopensc.so.6下载到/ tmp /;
2. 将opensc-pkcs11.so的修改版本下载到任意位置;
3. 添加opensc-pkcs11.so,将其作为Firefox中的安全设备;
指定共享对象的路径后,Firefox将加载PKCS11模块并执行以下代码。
上面的屏幕截图显示,xterm已经成功生成。
总结
当需要执行代码并且将Firefox作为单个Kiosk应用程序运行时,可以使用以上所描述的技术。
虽然使用这个方法之前,需要提前创建一个执行自定义代码的共享对象,但它是跨平台的,而且一旦库被下载到目标系统,调用它就非常容易。