SQL 注入原理

SQL 注入原理

sql 注入的原因是应用程序对用户输入的数据没有进行合法判断,并且直接将数据传给数据库进行查询。

流程如下:

  • 【 P 】:「用户输入」数据 P
  • 【 Q = X + P 】:「应用程序」将 P 与其他数据拼接
  • 【 Q --> R 】:「数据库 」将 Q 作为 SQL语句执行,返回结果 R
  • 【 R 】:「应用程序」接收数据 R,将 R 返回给用户

基于查询注入

如果应用程序会直接返回查询的结果,那么我们将可以使用基于查询的注入方法。

联合查询法:

  • 【 P 】:「用户输入」数据 P = -1' union select @@version -- [1]
  • 【 Q = X + P 】:「应用程序」拼接数据 Q = SELECT sc FROM score WHERE id='-1' union select @@datadir -- '[2]
  • 【 Q --> R 】:「数据库 」将 Q 作为 SQL 语句执行,返回结果 R = /var/sql/233.db
  • 【 R 】:「应用程序」接收数据 R,将 R 返回给用户

基于报错注入

应用程序不返回查询结果,反而返回 sql 数据库的报错信息。其流程如下:

  • 【 P 】:「用户输入」:P = ' and updatexml(1,concat(1,database()),1)#
  • 【 Q = X + P 】:「应用程序」:Q = SELECT sc FROM score WHERE id='' and updatexml(1,concat(1,database()),1)#'[3]
  • 【 Q --> R 】:「数据库 」:Q --> R = updatexml 执行错误的信息,其中带有 concat(1,database()) 的执行结果。
  • 【 R 】:「应用程序」:将 R 返回给用户

基于布尔注入

应用程序不会返回查询结果,而是会返回一个状态。比如:

  • 0 , 1
  • 真, 假
  • 高,中,低(当然三种状态)

其中前两个只有两种状态的信息被称为布尔值。流程如下:

  • 【 P 】:「用户输入」:P = 1' and length((select @@version)) > 0 #[4]
  • 【 Q = X + P 】:「应用程序」:Q = SELECT sc FROM score WHERE id='1' and length((select @@version)) > 0 #'
  • 【 Q --> R 】:「数据库 」:Q --> R =「真」 或 「假」
  • 【 R 】:「应用程序」:将 R 返回给用户

基于时间注入

应用程序仅返回一种状态,意为当前查询结束。但是没有返回任何数据或者其他状态。流程如下:

  • 【 C 】:「计时程序」用户开始计时 C
  • 【 P 】:「用户输入」P = 1' and length((select @@version))>0 and sleep(5) #
  • 【 Q = X + P 】:「应用程序」Q = SELECT sc FROM score WHERE id='1' and length(version()) and sleep(5) > 0 #'
  • 【 Q --> R 】:「数据库 」Q --> R =「sleep(5),并返回数据」 或 「不执行,返回数据」
  • 【 R 】:「应用程序」将 R 返回给用户
  • 【 C 】:「计时程序」结束计时 C

可以看出,时间注入与布尔注入本质相同。只是前者返回的是 「无延迟,查询结束」与「延迟 5 秒,查询结束」这两种特殊的状态。

二次注入

之前的注入都是一次输入,一次输出式的。而二次注入,则是两次输入,两次输出。

用一个 web 应用程序举例,假设它的业务逻辑为:

  • 【 P 】:「用户输入」数据 P
  • 【 P --> P' 】:「应用程序」将 P 中的特殊字符进行转义,得到 P'
  • 【 Q = X + P' 】:「应用程序」将 P' 与其他数据拼接
  • 【 Q --> R 】:「数据库 」Q 执行后,P' 将转换[5]为 P 并保存。同时返回一个结果信息 R
  • 【 R 】:「应用程序」将 R 返回给用户
  • 第二次查询
  • 【 R 】:「用户输入」数据 R
  • 【 R --> R' 】:「应用程序」将 R 中特殊字符转义,得到 R'
  • 【 Q2 = X2 + R' 】:「应用程序」将 R' 与其他数据拼接
  • 【 Q2 --> P 】:「数据库 」Q2 执行后,返回结果信息 P[6]
  • 【 Q3 = X3 + P 】:「应用程序」将 P 与其他数据拼接(注意:数据 P 特殊字符没有被转义!)
  • 【 Q3 --> R2 】:「数据库 」Q3 执行后,返回 R2
  • 【 R2 】:「应用程序」将 R2 返回给用户

从业务看出,第二查询的过程中,应用程序在拼接数据 P 时没有对 P 进行转义。这意味着我们可以在第一次查询时,将注入语句 P 保存进数据库,在第二次查询执行含有 P 的语句。来成功注入。


  1. 两个减号之后是有一个空格的。如果这数据通过 url 传输的话。需要将空格等价替换成 + 再传输。 ↩︎

  2. 可以观察到数据 P 在 Q 末尾的 WHERE 中,这不是必然,而是常见情况。特殊状态下,P 可能在 SELECT、GROUP BY 等之中。 ↩︎

  3. updatexml 函数需要三个参数,因此 concat(1,database()) 前后的 1 只是占位置的参数。 ↩︎

  4. 如果这里 select @@version 不加括号,将会被 length 认为是变量名。引发语法错误。 ↩︎

  5. 数据库在存储数据时,会自动去掉数据中的转义符号。 ↩︎

  6. 这里的结果 P 就是第一次访问时保存的数据 P。 ↩︎

原文地址:https://www.cnblogs.com/shiwai/p/14172157.html