通行证│用户名: 密码: 验证码: 验证码,看不清楚?请点击刷新验证码 电信网通铁通移动   在线
文章搜索:
热门搜索:红客 黑鹰 红客技术 安全动画 红客培训
首页 文章 软件 动画 资源 励志 论坛 邮箱 会员 军事 科技 博客 爱心红客 最近更新 800g资源
 业内新闻 漏洞公告 病毒公告 电脑知识 网络知识 菜鸟入门 攻防教程 黑客攻防 安全编程 工具使用 综合安全 个人安全 安全相关 Q Q安全 原创精华 红客人物 站内事件
您现在的位置: 爱国者安全网 >> 文章类 >> 原创精华 >> 文章正文
推荐:我是如何开发CnSaferSI的
责任编辑:古典辣M°   更新日期:2005-10-14
 

适合读者:脚本攻击爱好者、程序员

前置知识:SQL基本语法、C#基本语法

Socket:CnSaferSI是最近圈子内涌现的功能比较完善的一个新注入工具,它的开发者是曾经发现动网BBS7漏洞的华仔和他的朋友Hoky,现在华仔已经开始投身到了专业软件开发的道路上,并准备为这个事业“抛头颅、洒热血”!最近很多朋友反映CnSaferSI使用起来很不错,希望我们能介绍一下它的使用方法和编写程序的思路,于是今天特别策划了这个文章,看看CnSaferSI是如何实现各种完备的功能的,同时也给喜欢编程的朋友提供一种编写优秀注入工具的思路,希望能对大家有所裨益。

我是如何开发CnSaferSI的

 

    最近一段时间闲着无事,就跟Hoky商量写一个比较通用的SQL注入程序,由于以前分析过NBSI的实现方式,所以最初的时候估计难度不会太大,同时使用C#这种面向对象机制健全的语言进行开发会更加得心应手。然而事实却经常跟我开玩笑,底层的注入程序在一个月内就完成了针对三种脚本(ASP,ASP.NET,PHP)和四种数据库(Microsoft SQL Server,Oracle 8i&9i,Access,MySQL)的基本注入功能,但由于我对Winform程序不熟悉造成了诸多未预期的困难。在这段时间内虽然遭受了不少打击,但是我知道有许多人在期待着它的完成,所以我会继续完成下去,如果哪位朋友对这个软件有兴趣的话可以跟我联系。

    下面我就说一说我在开发过程中的一些收获,希望对你有一定的帮助。

    数据库类型检测

    由于涉及到了四种数据库,而在注入过程中对于每种数据库所使用的注入方法都有很大的不同,所以在漏洞检测的同时,数据库类型的检测在这个过程中显得尤为重要。

    数据库类型的检测基本思路就是使用每种数据库脚本语言中所特有的函数进行检测。例如检测SQL Server数据库时是使用的:http://www.site.com/page.asp?id=1 and 0http://www.site.com/page.asp?id=1 and user>char(0)和http://www.site.com/page.asp?id=1 and user数据库类型的探测方法如下表1、2所示:< 1="1”和“and">

表1 在报错的情况下使用的参数

 

  表2 在不报错的情况下使用的函数


 

    漏洞类型的检测

    以前我们最常用的也不过是两种漏洞:数字型和字符型,在NBSI2中为了增强适用性提供了三种漏洞检测——即在原有的基础上增加了搜索型漏洞(这三种漏洞的成因在此就不介绍了,请大家关注以前黑防发表的文章),而我在检测索尼中国的网站漏洞时,分明已经确定了漏洞存在却无法在这三种漏洞中找到对应的类型。偶然间我想到了在SQL语言中可以使用“in”关键字进行查询,例如“select * from mytable where id in(1)”,括号中的值就是我们提交的数据,它的结果与使用“select * from mytable where id=1”的查询结果完全相同。所以访问页面的时候在URL后面加上“) and 1=1 and 1 in(1”后原来的SQL语句就变成了“select * from mytable where id in(1) and 1=1 and 1 in(1)”,这样就会出现期待已久的页面了。暂且就叫这种类型的漏洞为“包含数字型”吧,聪明的你一定想到了还有“包含字符型”呢。对了,它就是由于类似“select * from mytable where name in(‘firstsee’)”的查询语句造成的。

    如果你经常使用Access作为数据库写程序的话应该会知道在查询日期值的时候可以使用类似“select * from mytable where addDate=#2004-12-28#”的语句进行查询,其中的日期值如果未经过滤的话也就会造成另外一种漏洞——日期型注入漏洞。访问页面的时候在URL后面加上“# and 1=1 and #2004-12-28#=#2004-12-28”之后就会出现与之前访问的页面内容相同的页面了。但这种类型的漏洞由于其天生的局限性,在实际中很少出现(除非写程序的人是初学者或者一时糊涂),所以在此我只是从理论和测试上给大家提出来,如果真的遇到这种情况也不要不知所措。

    三种基本数据类型

    在注入的过程中有三种数据类型:Boolean,Int,String——它们是实现数据库内数据查询的最基本的数据类型,因此针对每种数据库来说实现这三种数据值的获取是整个注入程序的基础。每一种数据类型值的获取都是以前一种数据类型值得获取为基础。而整形值的获取是最关键的部分,可以通过对它的获取方法的不断改进以减少网络访问的次数达到提高整个注入效率的效果。

    1. Boolean值的获取

    一般这种数据类型只有在关键字存在的情况下才可以实现,它也是除SQL Server报错以外所有情况下获取其它两种数据类型的依据。用函数实现如下:
public bool GetBoolValue(string strBoolExpression)
{
string strResponse="";
m_http.Uri=this.m_strGetInfoUriTemplate.Replace("$SQL$",strBoolExpression);
strResponse=m_http.getResponse();
if(this.m_SiteInfo.CrackMethod==CrackMethod.SHOWERROR&&m_http.ErrorCode!=200)
{
return false;
}
return (this.m_SiteInfo.KeyInTrueCondition&&strResponse.IndexOf(this.m_SiteInfo.KeyWord)>=0)
/*关键字位于True条件中*/ ||(!this.m_SiteInfo.KeyInTrueCondition&&strResponse.IndexOf(this.m_SiteInfo.KeyWord)<0)
/*关键字位于False条件中*/;
}

    2. INT值的获取

    在一般情况下我们可以从0到65535循环,尝试整形值表达式的实际值,例如:
http://www.site.com/page.asp?id=1 and 5=(select count(*) from mytable)
直到出现与“and 1=1”访问的页面出现相同的关键字为止,但是在实际使用中这种方法速度极低,平均获取一个整数值的网络访问次数在40次以上,这种方法只有在完全可以确定实际值所在范围的宽度在10以内的时候才会采用。具体实现代码如下:
protected int IncreaseGetIntValue(int nAreaStart,int nAreaEnd,string strIntExpression)
{
for(int i=nAreaStart;i{
if(this.GetBoolValue(i.ToString()+"="+strIntExpression))
 {
  return i;
 }
}
return -1;
}

    为了改变这种局面我采用了步增法确定实际值范围,然后使用二分法确定表达式的精确值。然而,即使这样仍然不能满足实际的需要。在确定一个字符串的值的时候,我们通常把每一个字符先转化成Unicode值以后,再通过获取这个整数值来确定字符(包括各种地域性文字)。因为在ASCII码为0-32和127-256的两个区域基本上不会用到,所以仍然使用上面提到的方法就会有多余的网络访问的产生。为了避免这种情况的出现,可以根据人们的字符使用习惯采用分区域的二分法确定字符的值。例如可以将搜索的区域的顺序定义为:小写字母->大写字母->数字->标点符号->地域性文字,我称之为标准区域顺序,这是对所有字符的出现概率进行整体评价所得到排序。实际上在NBSI2的最初版本中就已经实现了这种确定字符的方法。

    Socket:步增法没听说过?那就去买一本数据结构或者算法入门的书看一看吧,限于篇幅我就不多说了。这种方法在表达式的值小于13的时候可能会比递增循环的方法效率略低以外,在实际值较大的情况下效率得到了极大的提高。如果有兴趣的话你可以计算一下两种算法之间的复杂度都是多少,这样的话就会一目了然了。

    不过也不要高兴得太早了,我用这种方法注入一次万网需要两个小时的时间,这样的速度恐怕你我都难以忍受吧?!抛开具体的实现不看,从一个开发者的角度考虑,我通常会使用一些比较容易让人看懂的、容易理解的名称作为表名、列名或者文件名、键名,或者在命名的时候使用下划线分隔单词,特殊地在Oracle中所有的表明和列名都不能使用小写字母。因此如果仍然使用标准区域顺序进行搜索的话未免显得太蠢了,我想傻根也不会这么做的。这是由于现有的数据和信息没有被合理使用所造成的,因此怎么样使用现有的数据,怎么样将现有的信息应用到猜解过程中成为了提高效率的关键问题。

    为了充分利用现有的数据和信息,我采用了动态区域顺序的方法。也就是在注入的过程中每得到一个字符都要针对当前已经得到的数据和环境信息调整一次区域顺序,以使下一个字符出现概率大的区域向前靠,而使概率小的区域向后靠。区域顺序调整的方法可以依据我们的词语使用习惯也可以依据用户的预先定义,实现起来比较灵活。例如在猜解Oracle数据库的结构时如果已经得到的表名中包含ADMIN_USER和ADMIN_GROUP,而在猜解另外一个表名时在得到了前三个字符为“ADM”的情况下猜解下一个字符时“I”的优先度毫无疑问是最大的。使用这种方法进行了改进之后,完成一次对万网的注入仅用20分钟就可以完成,在某些情况下猜解一个字符仅仅使用一次网络访问就可以完成。

    虽然当前的注入方法已经得到了许多的改进,但是我相信应该还会有更好的方法和更高的效率有待去发现。只有我们大家去想去摸索去实践,技术才会得到进一步的发展。天上的鸟粪不会刚好掉到你头上,当然我也不会那么幸运。

    3. String值的获取

    在解决了Int值的获取之后,字符串的值就相对容易多了。只要循环每个字符取得它的Unicode值之后拼接起来就可以了。不要忘了在SQL Server数据库报错的情况下可以直接从错误信息中得到字符串的值哦,这可是比任何方法都快的。

    多国语言问题的解决

    有人曾经跟我抱怨,注入英文网站和中文网站的时候还没有关系,但是注入日本或者台湾网站的时候得到的全是乱码,这个问题在NBSI2中也会出现。经过我的分析,发现在程序对网络数据进行解析的时候使用操作系统的默认语言类型(简体中文),从而导致了其它语种显示为乱码的现象。在IE浏览器中选择查看编码就会避免这种现象的发生。因此在对网络数据进行解析的时,只要使用适当的代码页(Code Page)就会得到更具有可读性的结果。代码页的相关知识在MSDN和CSDN上面都有详细地介绍,如果有兴趣可以去搜索一下。

    解决过滤特殊字符的问题

    在实际注入中大家也许经常会遇到漏洞检测已经成功,却由于过滤单引号或者空格而中途放弃的情况,难道就此罢手吗?我们要大胆的Say NO!
 对于过滤空格的问题解决起来比较容易,只要在需要用空格的地方使用闭合的注释进行填充就可以了,例如在SQL Server数据库中这样构造查询语句:
Select/**/mycolumn/**/from/**/mytable/**/where/**/name=’firstsee’
在解决过滤单引号问题时,不同的数据库会有一些差异。在SQL Server数据库需要将字符串转成Binary类型的数据,例如:
Select mycolumn from mytable where name=0x66006900720073007400730065006500
也就是将每个字符的Unicode值都变成16进制的数字,你也可以使用SQL Encode进行转换,但是对Unicode值大于255的字符时使用时会出现问题。
在MYSQL中需要将字符串转成Char函数的形式,例如:
Select mycolumn from mytable where name=char(102,105,114,115,116,115,101,101)
在Oracle中需要将字符串转成Chr函数连接的形式,例如:
Select mycolumn from mytable where name=chr(102)||chr(105)||chr(114)||chr(115)||chr(116)||chr(115)||chr(101)||chr(101)
在Access中需要将字符串转成Chr函数连接的形式,例如:
Select mycolumn from mytable where name=chr(102)+chr(105)+chr(114)+chr(115)+chr(116)+chr(115)+chr(101)+chr(101)

Select mycolumn from mytable where name=chr(102)&chr(105)&chr(114)&chr(115)&chr(116)&chr(115)&chr(101)&chr(101)

    数据库结构的获取

    数据库结构的获取可以分成表名的获取和列名的获取,实现方法大体可以分为系统表(视图)查询和字典搜索两种方法。在SQL Server中所有的表名存储在当前数据库的Sysobjects表中,并且Xtype列的值为“U”时的Name列的值为用户表名,列名存储在当前数据库的Syscolumns表中并且ID列的值与它所在的表名所对应的Sysobjects表中的ID列的值相等;在Oracle中所有的用户表信息可以通过USER_TABLE视图查询得到,所有的列都可以通过查询COLS视图得到,在这里需要注意的是在Oracle中不可以使用TOP关键字,而是通过Rownum列值来限定查询结果中记录的个数;在Access和MYSQL中主要是通过字典式搜索尝试来确定数据库中的表名,这是因为在Access和MYSQL中的数据库结构信息是以二进制的形式存储在文件中的,无法通过SQL语句查询得到(在ASP中可以通过ADO的OpenSchema方法获取Access的结构信息)。通过以上分析可以得到获取各种数据库结构的查询语句如表3所示。


表3 获取表名和列名所使用的SQL语句


①[Index]表示当前要查询的对象的序号
②[TableName]表示当前要查询的列所属表的表名
③[ColumnName]表示当前测试的列名

    脚本注入在程序中的实现方法就说这么多了,这些都是在一般用户权限下都可以实现的功能,所以应用的也比较多。至于在更高权限才会成功的文件上传、注册表管理、文件系统管理、CMD命令执行等功能,也是在以上所提到的方法的基础上灵活运用存储过程实现的。这些大家已经应该耳熟能详了,我就不再多做介绍,要不然老编又要说我骗稿费了。看完了这篇文章之后你也应该能写出来一个具有基本注入功能的程序了吧,期待你的好消息!

 

 

  • 上一篇文章:
  • 下一篇文章:
  • 最近更新
    固顶文章 爱国者安全网2007年度优秀版主评选
    普通文章 瑞星公司01月11日发布 每日计算机病毒及木马播报
    普通文章 破解博彩神助(专注彩票) V2.8.01
    推荐文章 推荐:跨站脚本执行漏洞代码的六点思路
    普通文章 Windows系统下的远程堆栈溢出 实战篇
    普通文章 Windows系统下的远程堆栈溢出 原理篇
    普通文章 MsSQLServer是如何加密口令的
    普通文章 浅谈国内的渗透评估过程
    普通文章 Dvbbs8.1 0DAY(通杀Access和mssql版本)
    普通文章 微软:我们的代码比赛门铁克更安全
    热门文章
    普通文章REAL蛀虫利用播放器漏洞下载恶意程序
    普通文章李彦宏:中国要在互联网领域逐渐超越美国
    普通文章马云:阿里巴巴的成功是一个生态链的成功
    普通文章Ingres用户认证非授权访问漏洞
    普通文章TCPreen FD_SET()函数远程栈溢出漏洞
    普通文章Winace UUE文件解压堆溢出漏洞
    普通文章Pclxav木马猎手第一代特征码引擎源代码
    普通文章IE收藏夹管理小精灵算法分析
    普通文章Extra Drive Pro算法分析历程
    普通文章雨过天晴自我注册
    精彩专题