'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")),"'","''")