荷兰研究人员Luke Paris创建了一个隐藏在PHP服务器模块中的rootkit,攻击者可以利用这个rootkit接管Web服务器,接管过程是通过一个很少使用的攻击向量(Apache模块)实现的。
rootkit 本身是一段代码,一般可以在操作系统的最低层级上运行,拦截内核操作并注入恶意动作。

常见的rootkit都是围绕着操作系统内核工作,级别高、手法熟练的攻击者才能在不破坏受害者计算机的情况下运用rootkit。而正是rootkit这种对C和C ++编码技术的较高要求,才促使Luke Paris 创建了一个不需与操作系统内核交互而是直接与PHP解释器交互的rootkit。
使用PHP的原因
Paris表示:PHP是web上最常用的编程语言之一。
在PHP模块中编写rootkit的首要原因是它的易用性。 学习如何使用Zend引擎(整个PHP语言构建的框架)比学习如何编写内核模块要容易得多,因为PHP的代码库本身更小,可以更好的记录内容,且不那么复杂。
即使没有好的参考文档或教程,我也能在一天内学习完编写PHP模块的基础知识。 如果我这个新手可以做到,那么那些想要实施入侵的黑客就更容易做到。
PHP rootkit可以让攻击者获得被攻击服务器上的持久接管权,而且不会被检测到。Paris表示,利用PHP模块隐藏rootkit是个很聪明的做法,结果也很有效。他列出了以下几点原因:
易用性 – 在PHP模块中编写rootkit比学习如何编写内核模块要容易得多,而且开发人员可以使用较少的代码写出rootkit。Paris表示自己已经在一天内学会了编写PHP模块的基础知识。
稳定性 – 常见rootkit多在内核空间中运行,这意味着如果编写水平较差,则可能会使整个系统崩溃。而使用PHP rootkit可以解决这个问题,写得不好的PHP rootkit也不会使整个系统崩溃。
“一个PHP rootkit顶多可能导致一个分段错误,但只会中断当前的请求,可以让攻击持续的时间更长一些(注意:大多数Web服务器会在错误日志中报告这一点,所以这也有可能引起怀疑)”
可逃避检测 - PHP rootkit很难被检测到,因为PHP模块中缺乏相应的检查机制。很少有开发人员会检查PHP模块的哈希值,因此,可以很轻易诱使开发人员下载被入侵的PHP模块或者在被入侵的服务器上替换PHP模块。
轻便性 – PHP rootkit是跨平台的rootkit,因为PHP本身也是跨平台的,而且PHP模块可以针对不同的平台进行编译。
此外,内核rootkit要求开发者为每个进程挂接系统调用,这会使主机的速度大幅减缓,降低主机性能,这可能会引起更多怀疑。但PHP rootkit只需要hook一个系统进程(就可以实现攻击)。
PoC 内容
Luke Paris在GitHub上发布了一个开源的 PHP rootkit PoC  。 这个PoC代码与PHP服务器的“hash”函数和“SHA1”函数挂钩,整个rootkit仅由80行代码组成,很容易将其隐藏在合法模块中。

为了防止攻击者使用以上代码,Paris省略了其中一部分内容,如果不是专业PHP开发人员,编译起来可能更加困难。但服务器管理员还是要考虑潜在的攻击向量。
PHP rootkit可以实现攻击持久性
大多数人不会考虑在被入侵服务器的PHP模块中查找恶意代码,因为这里通常很少会隐藏恶意软件。通常,他们会在公共网站的源代码、.htaccess文件或存储在网络服务器目录中的其他文件中查找恶意代码。
而Paris创建的这个PHP rootkit则可以实现攻击持久性,让恶意行为在不被检测到的情况下长期隐藏于最底层。
值得一提的是,Google研究人员破解了SHA1哈希函数,创建了具有相同SHA1哈希值的两个文件。 这让攻击者有机会添加PHP rootkit,然后生成具有相同哈希值的Apache模块(至少在理论上,尚未有人完成这种操作)。
正是如此,Airship项目专家Arciszewski也对此表态,他建议别再使用SHA1哈希函数,改为使用SHA256哈希函数。同时,他认为要采取强有力的措施处理涉嫌感染恶意软件的服务器,因为大多数服务器管理员都不了解这种新的攻击向量。
如果你正在清理受损系统,我们建议您在取证完成后,建立一个新的、干净的环境,并将数据从受信任的备份中迁移出来。
防范建议
为了防范这种PHP rootkit带来的攻击,Paris给了管理员一些建议:
在安装PHP后,保留模块的哈希列表;
管理员可以使用计划任务来计算扩展目录中所有文件的哈希值,并将其与当前的哈希值进行比较。
Paris还发布了一个Python脚本,用于检查用户PHP模块的SHA1哈希值。
脚本如下:
from argparse import ArgumentParser
from subprocess import Popen, PIPE
from hashlib import sha1
from os import path, walk
import re
import os
from sys import stderr
def extension_dir():
return Popen(["php-config", "--extension-dir"], stdout=PIPE).stdout.read().decode().strip()
def hash_files():
for root, dirs, files in walk(extension_dir()):
for file in files: