注入操作系统命令

注入操作系统命令

大多数Web服务器平台发展迅速,现在它们已经能够使用内置的API与服务器的操作系统进行几乎任何必须的交互。如果正确使用,这些API可帮助开发者访问文件系统、连接其他进程、进行安全的网络通信。但是,许多时候,开发者选择使用更高级的技术直接向服务器发送操作系统的命令。由于这些技术功能强大、操作简单,并且通常能够立即解决特定的问题,因而具有很强的吸引力。但是,如果应用程序向操作系统命令传送用户提交的输入,那么就可能会受到命令注入攻击,由此攻击者能够提交专门设计的输入,修改开发者想要执行的命令。

长用户发出操作系统命令的函数,如PHP中的exec和ASP中的wscript.shell函数,通常并不限制命令的可执行范围。即使开发者准备使用API执行相对善意的任务,例如,列出目录的内容,攻击者还是可以对其进行暗中破坏,从而写入任意文件或启动其他程序。通常,所有的注入命令都可在Web服务器的进程中安全运行,它具有足够强大的功能,使得攻击者能够完全控制整个服务器。

许多非定制和定制Web应用程序中都存在这种命令注入缺陷。在为企业服务器或防火墙、打印机和路由器之类的设备提供管理界面的应用程序中,这种缺陷尤为普遍。通常,由于这类程序由于操作系统交互的特殊需求,导致开发者直接使用合并了用户提交的数据的系统命令。

通过Perl注入

以下面的Perl CGI代码为例,它是一个用于服务器管理的Web应用程序代码的一部分。这项功能允许管理员在服务器上指定一个目录,并查看它的磁盘使用情况:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/perl
use strict;
use CGI qw(:standard escapeHTML)
print header, start_html("");
print "<pre>"

my $command = "du -h --exclude php* /var/www/html"
$command=$command.param("dir");
$command=`$command`;
print "$command\n";

print end_html;

如果按设想的方式运行,这段脚本将把用户提交的dir参数值附加在预先设定的命令后面,执行命令并显示结果。

然而,通过提交专门设计的、包含shell元字符的输入,攻击者可对这项功能进行各种方式的利用。对处理命令的解释器而言,这些字符有着特殊的含义,并可破坏开发者想要执行的命令。例如,管道符“|”可用于将一个进程的输入重定向为另一个进程的输入,从而将几个命令连接在一起。攻击者可以利用这种行为注入另外一个命令并获得输出结果。

通过ASP注入

以下面的C#代码为例,它是一个用于管理Web服务器的Web应用程序代码部分。该功能允许管理员查看被请求的目录和内容。

1
2
3
4
String dirName = "C:\\filestore\\" + Directory.Text;
ProcessStartInfo psInfo = new ProcessStartInfo("cmd", "/c dir " + dirName);
...
Process proc = Process.Start(psInfo);

如果按设想的方式运行,这段脚本将把用户提交的Directory参数值插入到预先设定的命令中,执行命令并显示结果。

和前面易受攻击的Perl脚本一样,攻击者可以使用shell元字符破坏开发者预先设定的命令,并注入他自己的命令。&字符用于将几个命令组合在一起。提交一个包含&字符的文件名和另外一个命令就可以执行该命令并显示器结果。

通过动态执行注入

许多Web脚本语言支持动态执行在运行时生成的代码。这种特性允许开发者创建可根据各种数据和条件攻台修改其代码的应用程序。如果用户输入合并到可动态执行的代码中,那么攻击者就可以提交专门设计的输入,破坏原有数据,指定服务器执行自己的命令,就好像这些命令是由最初开发者编写的一样。这时,攻击者的第一个目标通常是注入运行操作系统命令的API。

PHP函数eval可用于动态执行在运行时传送给该函数的代码。下面以一个搜索功能为例,该动能允许用户创建保存的搜索,然后在用户界面上以链接的形式丰台生成这些搜索。用户使用下面的URL访问该搜索功能:

/search.php?storedsearch=\$mysearch%3dwahh

服务器端应用程序通过动态生成变量来执行这项功能,生成的变量包含在storedsearch参数中指定的名/值对;此处,它创建值为wahh的变量mysearch。

1
2
$storedsearch = $_GET['storedsearch'];
eval("$storedsearch");

这时,就可以提交专门设计的输入,由eval函数动态执行,从而在服务器端应用程序中注入任意PHP命令。分号字符可用于在单独一个参数中将几个命令连接在一起。例如,要检索文件/etc/password,keyi shiyong file_get_contents命令或system命令:

1
2
/search.php?storedsearch=\$mysearch%3dwahh;%20echo%20file_get_contents('/etc/password')
/search.php?storedsearch=\$mysearch%3dwahh;%20echo%20file_get_contents('cat%20/etc/password')

查找OS命令注入漏洞

不同的命令解释器处理shell元字符的方式各不相同。理论上,任何类型的应用程序开发平台或Web服务器可能会调用任何shell解释器,在它自己或任何其他主机的操作系统上运行。因此,不应根据对Web服务器操作系统的了解,对应用程序如何处理元数据做出任何假设。

有两种类型的元字符可用于在一个现有的预先设定的命令中注入一个独立的命令。

  • 字符 ; | & 和换行符可用于将几个命令逐个连接在一起。有些时候,可以承兑使用这些字符以达到不同的效果。例如,在Windows命令解释器中,使用&&则第二个命令只有在第一个命令成功执行后才会运行。使用 || 则总运行第二个命令,无论第一个命令是否成功执行。
  • 反引号(`)用于讲一个独立的命令包含在最初的命令处理的数据中。把一个注入的命令放在反引号内shell解释器就会执行该命令,并用这个命令的结果代替被包含的文本,然后继续执行得到的命令字符串。

通常,检测命令注入是否可行的最可靠方法就是使用时间延迟推断,类似于前面描述的利用盲目SQL注入时使用的方法。如果一个潜在的漏洞可能存在,那么就可以使用其他方法确定这个漏洞,并获得注入命令的执行结果。

  1. 通常可以使用PING命令让服务器在一段时间内检测它的回环接口,从而触发时间延迟。Windows和Unix平台在处理命令分隔符与PING命令方面存在一些细微的差别,但是,如果没有设置过滤,下面的通用测试字符串应该能够在两个平台上引起30秒的时间延迟。

    || ping -i 30 127.0.0.1 ; x || ping -n 30 127.0.0.1 &

    如果应用程序过滤掉某些命令的分隔符,为加大监测到命令注入漏洞的可能性,还应轮流向每一个目标参数提交下面的每个测试字符串,并监控应用程序进行响应的时间。

    | ping -i 30 127.0.0.1 |

    | ping -n 30 127.0.0.1 |

    & ping -i 30 127.0.0.1 &

    & ping -n 30 127.0.0.1 &

    ; ping -i 30 127.0.0.1 ;

    %0a ping -i 30 127.0.0.1 %0a

    ‘ ping 127.0.0.1 ‘

  2. 如果发生时间延迟,说明应用程序可能易于受到命令注入攻击。重复几次测试过程,确定延迟不是由于网络延时或其他异常造成的。可以尝试更改-n或-i的参数的值,并确定经历的时间延迟是否会随着提交的值发生对应的变化。

  3. 使用所发现的任何一个可成功实施攻击的注入字符串,尝试注入另一个更有用的命令(如ls或dir),确定是否能够将命令结果返回到浏览器上。

  4. 如果不能直接获得命令执行结果,还可以采用其他方法:

    • 可以尝试打开一条通向自己计算机的带外通道。尝试使用TFTP上传工具至服务器,使用telnet或netcat建立一个通向自己计算机的反向shell,并使用main命令通过SMTP发送命令结果。
    • 可以将命令结果重定向到Web根目录下的一个文件,然后使用浏览器直接获取结果。

    dir > C:\inetpub\wwwroot\foo.txt

有时,由于某些字符被过滤掉,或者应用程序所使用的的命令的API的特殊行为,可能无法注入一个完全独立的命令。但是,攻击者仍然可以破坏所执行的命令的行为,得到想要的结果。

查找动态执行漏洞

动态执行漏洞最常见于PHP和Perl等语言。但基本上,任何应用程序平台都可能会向基于脚本的解释器传送用户提交的输入。

  1. 用户提交的所有数据项都可提交给动态执行函数。其中最常见的数据项是cookie参数名称和参数值,以及作为前一项操作结果保存在用户资料中的永久数据。
  2. 尝试轮流项目表参数提交下列值:
  • ;echo%20111111
  • echo%201111111
  • response.write%20111111
  • :response.write%20111111
  1. 监控应用程序的响应。如果字符串111111被单独返回,就表示应用程序可能易于受到脚本命令注入。
  2. 如果字符串111111并未返回,寻找任何表示输入被动态执行的错误消息;另外,可能需要对语法进行调整,以实现注入任意命令的目的。
  3. 如果攻击的应用程序使用PHP,可以使用测试字符串phpinfo()。如果它成功执行,应用程序将返回PHP环境的配置信息。
  4. 如果应用程序可能易于受到攻击,与前面描述的查找OS命令注入漏洞时一样,注入一些造成时间延迟的命令确认这一点。例如:

system(‘ping%20127.0.0.1’)

防止OS命令注入

通常来说,防止OS命令注入漏洞的最佳方法是完全避免直接调用操作系统命令。几乎Web应用程序所需要执行的每个任务都可以使用内置API完成,而且攻击者无法控制这些API,使其执行其他预料之外的命令。

如果无法避免要在传送给操作系统命令解释器的命令字符串中插入用户提交的数据,应用程序应实施严格的防御来防止漏洞发生。如果可能,应使用一份“白名单”限制用户只输入一组特殊的值。或者,应将输入范围限制为少数字符,例如,仅字母数字字符。应拒绝包含任何其他数据(包含任何元字符或空白符)的输入。

应用程序应使用命令API通过它的名称和命令行参数启动特殊的进程,而不是想支持命令链接与重定向的shell解释器传送命令字符串,从而实施另一层保护。

防止脚本注入漏洞

通常而言,防止脚本注入漏洞的最佳方法是,避免将用户提交的输入或者来自用户的数据传送给任何动态执行或包含函数。如果由于某种原因必须传送用户提交的输入,那么应对相关输入进行严格的确认检查以组织任何攻击。如有可能,使用一份由已知可靠的值组成的“白名单”,并拒绝任何没有出现在这个名单上的输入。如果无法做到这一点,应根据一组已知无害的字符[如字母数字字符(空白符除外)]检查再输入中使用的字符。

0%