设为主页 | 加入收藏 | 繁體中文

漠漠孤云尽成雨----浅谈CGI脚本安全

  漠漠孤云尽成雨
  ...........----浅谈CGI脚本宁静
  [I.T.S]Jambalaya
  很多朋侪告诉我一定要去不断总结本身学的工具,并且不要吝啬拿出来与人分享,这样才有一个很好的沉淀和交流,还有相互的促进。于是本身把本身一部分学习笔记整理了一下。还有一些本身平常总结的一些学习经验,希望对大家有所帮助。
  CGI脚本是在网络服务器上表明执行的,然后将执行的结果前往给客户端(便是我们的欣赏器)。由于perl的壮大功能、他的机动性和对所有盛行的操作系统的支持,固然还有它的“代价”(纯收费)。使其遭到了很多CGI编程者的喜爱。我们知道任何言语本身是无宁静性可言的,宁静只是存在于利用他的人的身上。所以要是在编写脚本时,没有宁静的认识,那么编写出的代码也就没有宁静的"功能"了。
  一、注意对变量的处置惩罚
  1、用户的输出是不可信托的,当谈到宁静编程的工夫,比尔盖茨老师对他的员工说了一句非常经典的话:All input is invalid.(所有的用户输出都是无害的,具体是不是他第一个说的,有待考察)
  一切用户输出的地方都是我们该当注意的地方。大家知道美国最闻名的电子商务网站e***.com吧,他在1999年就被黑客用以下的要领入侵了。我们的新浪网站也被用同一种要领攻了出来,并且还被人截了图。。。。
  来看一段代码吧:
  -----------Code Start------------
  #unsafecodz.cgi
  01 $filepath="f://myhome//bbs//"
  ......
  ......
  13 $filename=$query -> param('page');
  14 if ($filename eq "")
  15 { $filename="error.html";
  16 die("对不起,文件名不能为空!");
  17 }
  18 else{
  19 $filename="$filepath/".$filename;
  20 open(FILE,$filename);
  21 while()
  22 {
  23 print $_;
  24 }
  25 close(FILE)
  ......
  -----------Code Ends-------------
  这段代码根本上没有作太多改动,由于我演示是在本身的机子上,所以把途径改了一下,我们在这里回放一下曾经的历程。
  这段代码是网站用来欣赏的其他网页的,如:http://127.0.0.1/myhome/bbs/unsafecodz.cgi?page=something.html,就会欣赏something.html这个文件,这里的$filename用param提取page中用户输出的内容,$filename其实是用户直接输出的内容,而代码对$filename并没有做严格的审查只是检查是否为空,要是歹意用户直接在欣赏器中指定其他文件,如cgi,asp大概任何文件,则前往的是文件的源代码,如:http://127.0.0.1/myhome/bbs/unsafecodz.cgi?page=unsafecodz.cgi。
  看到unsafecodz.cgi的源代码了,通过简略的欣赏其他的页面,我们可以根本上可以得到所有文件的源代码。我们也可以通过"../"来切换到其他的目录下。
  这还不是最糟糕的,要是你的系统是Linux大概UNIX,那么更太过的在这里呢!http://127.0.0.1/myhome/bbs/cgi-bin/unsafecodz.cgi?page=/../../../../../../ect/passwd。结果显而易见,可以看到了主机的用户名和暗码文档。
  这也不是最遭的,要是利用open函数加管道符执行任意命令的后果会怎么样?利用open函数执行命令的技术很老了,我就不废翰墨了。
  后来有人对代码举行加固,将第19行改成 $filename="$filepath/".$filename.".html" 他限定背面4位为html,但是这样的加固似乎起不到什么作用,由于它纰漏了NULL字符。提交如下请求依然可以绕过他的限定:
  /myhome/bbs/unsafecodz.cgi?page=unsafecodz.cgi%00.html
  还是这个题目,还有人这么加固代码,它在将19行改了之后,又将第14行改成:
  if (($filename eq "") || (-e $filename))
  他在这里检查文件是否存在,要是不存在就不去举行背面的操作,我们这么依然提交:
  /myhome/bbs/unsafecodz.cgi?page=unsafecodz.cgi%00.html
  你会发现我们依然可以成功。
  他还是纰漏了什么,他纰漏了什么呢?他纰漏了null字符是可以绕开-e的检查,也便是说(-e $filename)将会以为文件是存在的,由于%00背面的工具在-e中会被纰漏。
  ok,这里我们停一下,我们应该能注意到刚才是什么改变了程序原来的流程。便是谁人null字符(),想想还有什么我们可以用来改成程序的流程呢?\t,\r,\x0B,\n,空格。这些字符都很有用,大家记着它并要学会如何自由运用这些工具。大家是否还记得前不久的dvbbs论坛毛病,便是由于上传中的null字符所惹起的。很多工夫你会发现技术打破不过便是你的技术积聚沉淀后的发作。
  ##关键词:用户输出的变量
  ##检查:是否做过有效过滤
  2、隐式输出的危害
  用户的输出分为两种,显示输出和隐式输出。下面的例子没有注意到用户的输出招致题目的呈现。由于那是用户直接输出的,算是显示输出吧。如今很多程序员都会下认识的去掩护本身程序的宁静性大概他们本身就了解一些宁静的重要性,都会加一些有用大概没用的限制,用户直接输出可利用的部分越来越少,于是隐式输出就开始遭到存眷。这里说的并不仅仅是perl cgi,包括如今一大帮人玩的asp注入攻击,还有原来是n年前的技术如今才浮出水面的php注入还有一些其他的攻击手段。细致回顾一下你就会发现很多都是隐式输出所惹起。隐式输出都是不被程序员注意大概容易被纰漏的地方。
  cookie应该算是隐式输出中比较典型的例子,我们就用cookie来说事儿吧......
  -----------Code Start------------
  #unsafecodz2.cgi
  ......
  18 $filename=$query->cookie("namecookie");#指导****#
  19 $filename="$filepath/".$filename;
  20 open(FILE,$filename);
  21 while()
  22 {
  23 print $_;
  24 }
  25 close(FILE)
  -----------Code Ends------------
  注意打星号的那一行,这只是提取cookie罢了,这简直不是用户的直接输出,但这却是用户可以直接控制的。要是歹意用户通过nc提交如下东东,后果是什么呢?
  GET /myhome/unsafecodz2.cgi HTTP/1.1
  Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, */*
  Accept-Language: zh-cn
  Accept-Encoding: gzip, deflate
  User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
  Host: 127.0.0.1
  Connection: Keep-Alive
  Cookie: namecookie=/../../../../../ect/passwd
  我们看到这行中
  Cookie: namecookie=/../../../../../ect/passwd
  一样平常隐式输出都不容易被注意。
  这里攻击者可以通过结构cookie来控制网站,结果便是和下面的一样。说到这里其实都是一句话:All input is invalid。作为代码的编写者要注意所有用户可以直接或直接控制的地方。
  这里你可能觉得你似乎明白了,但我敢说其实你并没有真的明白,cookie的控制是内里最简略的一个例子。隐式输出很多工夫,不要说写代码的人了,就连很多WEB宁静的妙手都不见得能够非常容易的找到,他们更多的是凭本身的经验和直觉。你可能觉得我言过其实了,其实并没有,简略的连傻瓜都能看出来的隐式输出,固然容易找到。但是很多的危害更大的,并不那么容易发现,所以这个工夫多是依附本身的经验和对毛病"味道"灵敏的嗅觉。
  ##关键词:没有
  ##检查:是否做过有效过滤
  解决要领其实很简略,便是严格控制用户的输出。所谓严格控制并不只是过滤,由于过滤难免有丧家之犬。限定要比过滤来得轻巧和严格。把用户的输出控制在你规定的范畴内,可以用一个正则表达式来给用户同等个范畴,指定用户可以输出的字符大概数字,要是用户输出与你的规定的不匹配,则不与通过。
  至于如何做限定,用正则表达式会很简略。我用email的例子阐明:
  if (email !~/^[\w.-]+\@[w.-]+$))#要是反面内里的规定字符匹配则报错
  {
  &error"输出不正确,难道您便是传说中的黑客?"
  }
  else
  {
  #输出正确,继续操作。
  ......
  email的一样平常款式是fakename@fakename.org。我们只希望用户输出字符、数字、@、"."、“-”、“_”这些东东。永远不要幻想用户会按照你所希望的输出,除非你给他们规定范畴。在这里用以一个简略的正则表达式。在这个正则表达式中,要求用户只能输出英文大小写字符,数字和“@”,"-","_""."这几个字符,要是输出其他的,则报错。
  二、注意几个伤害函数在代码中的利用和特别字符过滤。
  1、有几个伤害函数在程序中用得越少越宁静(这么说好像有点不严格,呵呵)。由于很多都是黑客的打破口。这些函数是:system(),open(),exec()。
  system()和exec都可以执行系统命令,如system("del f:\myhome\$filname"),要是$filename也是通过表单从用户那里得到的,要是我们在$filename处输出1.txt;del f:\myhome。我们就删除了整个目录。我们如今可以删除任意文件。要是你用管道操作符的话也可以。
  其实system()函数可以执行系统命令,要是对其中变量缺少严格限制容易惹起宁静题目,程序员们或多或少的知道一些,但是由于疾速开发大概项目给的工夫紧,为了应付差事,每每不会理会。
  ##关键词:system()、exec()
  ##检查:函数中是否有可控制的变量,是否可利用
  open()函数也是我们该当留意的地方,大家不要误解,open函数本身是没有什么的,而是内里的用户输出的数据招致的题目会合到了open()函数上。open()函数原来是用来打开一些文件,我们看到我们的第一个程序便是由于open函数惹起的走漏源代码。我们还可以通过“>”,"<"这样的符号来控制是否向文件写入大概读出。要是加上">"则可以向文件中写入,要是对那些向cgi文件中写入的字符不加以控制大概过滤不完全,那么写入任意的语句就很伤害了。其实宁静毛病只不过是代码编写者没有思量到而歹意用户却想到了的地方。一切我们还是让代码本身说吧:
  ------------Code Start---------------
  ......
  my $file = "$lbdir" . "forum$inforum/$intopic.pl";
  ......
  if (open(FILE, ">$file"))
  {
  flock(FILE, 2) if ($OS_USED eq "Unix");
  print FILE "$intopic\t$topictitletemp\t$topicdescription\t$threadstate\t$threadposts\t$threadviews\t$startedby\t$startedpostdate\t$inmembername\t$currenttime\t$posticon\t$inposttemp\t";
  close(FILE);
  }
  ------------Code Ends------------------
  呵呵,这段代码大家眼熟吧,不错,这便是LB5K论坛post.cgi中的一段代码,我觉得这段代码应该最能阐明题目(其实是我手懒,懒的本身写了),大家注意这里的
  open(FILE, ">$file");
  打开文件准备向内里写入。背面的print便是向指定的文件中写入。论坛的本意是将用户的贴子标题简略的保存在一个.pl文件中作为保存,但是编写者没有注意到贴子的标题其实是用户可以任意控制的,要是歹意用户输出system函数执行任意指令,要是标题是system @ARGV那么所保存的.pl记载文件中的第一行也是system @ARGV,这样要是去调用谁人.pl文件就构成了一个webshell。
  别急,我还没说完。前面说过利用open()+管道符异样可以执行命令。
  ##关键字: open()和背面相联系关系的变量
  ##检查:open打开的文件是否可写,可写变量是否可以控制。变量是否可控制。
  2、特别字符
  大部分情况下在用户输出的相近,程序员为了掩护程序会过滤掉一大批特别字符。然后用户输出的歹意语句无法执行。这个部分其实要说的工具很多,但是总觉得本身有点啰里啰唆的觉得,为了节省版面简言之吧。
  看看下面的字符你是不是都过滤掉了:
  反引号(``)
  反引号我们平常利用的不多,但在perl使用中功能却很大,它象system一样可以执行系统命令,如:
  System('dir c:\')
  $a=`dir c:\`;print $a;
  上下两条语句执行的结果是一样的,就算过滤掉system,用反引号可以起到雷同的作用。
  逗号
  逗号可能是由于个头小的缘故,并没有遭到太大的器重,不象他的兄弟分号长得那么高总遭到人家存眷,但其实逗号很多工夫可以做很多事变,下面两句话意思是一样的:
  $a=`dir C:\`;print $a;
  $a=`dir C:\`,print $a#
  分号
  分号就不用说了吧,可以截断程序的流程,比如:
  system("del ./path/$file");
  假设$file可控,那么直接参加jam.txt;del ./path
  然后整个path目录就消失了......
  反斜杠
  反斜杆的使用很巧妙,正常的过滤下要是用户输出/../会被过滤成/\.\./,但是要是用户输出/\.\./呢?则被过滤成了/\.\./,看到了么?巧妙的输出,巧妙地躲避了规则限制。
  管道符
  知道这句语句在ls背面加上管道符(|)会起到什么作用么?
  open(FILE, "/bin/ls")
  知道的话我就不废话了。
  尖括号(<>)
  跨站脚本是怎么完成的你不会忘记吧?^_*
  美元符($)
  这个放在perl中的字符串前面是什么意思?要是用户用这个字符结构语句输出,又会孕育发生什么结果?你不会不知道吧:p
  换行符等
  \t,\r,\x0B,\n,还记得第一个例程我们是怎么改变程序的流程的么?
  其实这只是一部分,限于篇幅不多说了。
  三、验证的不完全
  验证不完全和上下文逻辑错误是程序员最容易犯的错误。作WEB宁静要是能有一个严谨的思维那就再好不过了,由于一个严谨的思维来去对付那些逻辑错误应该是能很容易发现(个人以为)。要是你不幸和我一样没有一个成熟而又严谨的思维,那么就多练习多总结来增长本身的经验值吧。勤能补捉阿!最容易纰漏的每每危害是最大的。几个大的cgi论坛都曾经有过这个题目。其实所谓验证不完全便是一种逻辑思维本领的不完善。建议去学一下分离数学:-D。
  举例阐明:
  ------------Code Start---------------
  #adminchecking.cgi
  #这是一段管理员查验认证的入口程序
  ......
  $membername=cookie("inputname"); #从cookie中得到名字和暗码
  $memberpassword = cookie("inputpassword");
  $filetoopen="$filepath/"."$membername.cgi";#提取保存在$membername.cgi中的用户名和暗码
  if (-e "$filetoopen") #检查这个用户文件是否存在
  {
  open(FILE,"$filetoopen");
  $line=;
  close(FILE);
  ($name,$password,$vip)=split(/\t/,$line);
  }
  if((lc($supername) eq lc($name)) && ($superpassword eq $password) && ($vip eq "super"))
  #$vip值来自param提交
  { #要是管理员名字、暗码和头衔(vip)都正确则可以进入。。。。
  ------------Code Ends------------------
  这里暂不讨论将暗码和管理员称号保存在$membername.cgi下是否宁静,我这里只是简略的举个例子。程序首先从cookie中提取了用户名和暗码,还有用户头衔。然后打开以这个用户为名的文件,提取保存在内里的admin的用户名,暗码和头衔,分别给$name,$password,$code。然后对这三者和输出的举行比对,都一样才可进入管理页面。好像没什么错误,但不知道你是否发现其实谁人if语句根本便是形同虚设,为什么这么说呢?要是cookie中用户名和暗码都为空,那么if这段语句根本不会被执行,更不要说打开用户文件了。这里最重要的是谁人头衔的值,要是用户名和暗码都为空,这时由于文件不存在所以if不会执行,也便是不会去提取用户的用户名、暗码、头衔着三个值,用户名和暗码都为空,空一定等于空,这个不用说了。那么就剩下一个头衔(vip)没有解决,由于头衔(vip)是用户提交的,所以我们可以在欣赏器中直接指定vip=super,这样就成功了绕过了对管理员的查验认证。这便是我们所说的验证不完全,也是比较难制止的错误。
  大概你会说这样的危害只能影响到那些开源的源代码,那你就错了,通过差别页面差别参数的比较,发现类似如这种校验参数的利用并不是什么难事。这便是WEB入侵中的小本领。
  这里一路说上去,我们应该注意到:思维的拓展性很多工夫是来自于经验。要是你老是感叹为什么他人想得出来的要领本身想不出来,然后以为本身的思维太不开阔了,那我可以很负责告诉你,那不是你的思维不开阔,那绝对是你的经验不敷,而经验是哪里来的?他来自于实践:)
  ##关键字:没有
  ##检查:对管理员的认证查验的入口,和相关函数。
  四、未查验用户输出长度
  对于攻击者很多工夫并不都是象我这样的美意人,(呵呵,别用砖头丢我)有工夫他们只是为了攻击而攻击,这个工夫D.O.S(回绝服务攻击)就成了他们的首选。对用户的输出长度的查验就尤为重要了,如$ENV。要是你没有对用户输出作一个限定,那么当用户输出一堆垃圾信息时,就会孕育发生回绝服务攻击。更有甚者,用垃圾文件添塞硬盘,直到把硬盘写满为止,可骇不?别以为他人不会用这么笨的要领,前不久就有人写程序模仿IE正常访问,去革新不断调用数据库,招致数据库瘫痪。
  五、服务器的权限设置
  不要让你的WEB以管理员权限运转,IIS的默许设置是GUEST的,这种权限很低,就算攻击者得到了一个Webshell,也不会对你有太大的威胁,要是你的目录设置和服务器的设置装备摆设好的话,攻击者很难有大的作为。如IIS默许设置装备摆设是GUEST,Apeach的默许设置装备摆设是uid=99;nobody的权限。要是都是这样的话,这条便是废话了,但是我在给很多网站做测试的工夫发现不少服务器给WEB一个root权限,那根本上这个服务器要是被黑客攻击就算是OVER了。攻击者连提拔权限都不用,就可以从容的控制整个服务器。到工夫你想哭都找不到调儿。
  六、服务器的目录设置
  其实下面曾经谈到过了目录设置,这里细说一下,不要把所有的目录均设置为有脚本执行权限的。
  注意用户可以控制的目录区域,比如上传头像大概文件的目录
  歹意用户向服务器写入的shell要是写入了没有脚本执行权的目录中,谁人shell也就执行不了了。
  这篇文章到这里就结束了,这只是本身总结的一部分,其实还有很多工具限于篇幅和其他缘故原由没有写,比如定名规则之类的本领。大家可能曾经觉得到了,整篇文章并不是一篇完全的教学文章,我更希望能通过这篇文章来拓展你的思维。很多技术的拓展的根基还是你的根本功。学习工具的工夫没关系多问问本身:我真的明白了么?
  很多工夫你觉得本身曾经明白了,但其实你只了解到了皮毛罢了。
  当本身发现一个新的技术大概要领没关系和他人共享一下,很多工夫你觉得是大喜过望,但其实人家早就发现了。
  明确本身在应该学习什么?你是在学习技术本身还是在学习学习技术的思维?
  "漠漠孤云尽成雨",沉淀和积聚是促进你前进的动力
  废话太多了,就容易被wtf当成骗稿费的了。(这个尤为重要,切记,切记)
  行文匆匆,技术无限,如有错误,接待到www.itaq.org指教。
 


    文章作者: 福州军威计算机技术有限公司
    军威网络是福州最专业的电脑维修公司,专业承接福州电脑维修、上门维修、IT外包、企业电脑包年维护、局域网网络布线、网吧承包等相关维修服务。
    版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处 、作者信息和声明。否则将追究法律责任。

TAG:
评论加载中...
内容:
评论者: 验证码: