概述:MySQL8 密码生成逻辑,注入测试。

0x01 相关函数

  • mysqld!generate_native_password
  • mysqld!generate_sha256_password

如下所示:为创建密码为 admin123 生成的 mysql_native_password 的密码加密结果,*01A6717B58FF5C7EAFFF6CB7C96F7428EA65FE4C

create USER mnpweak1 IDENTIFIED WITH mysql_native_password BY 'admin123';
generate_native_password

看一下数据库入库结果,与上述函数计算结果一致。

image-20231207143814218

调用堆栈如下所示:

[0x0]   mysqld!generate_native_password   0xc9ce8f8128   0x7ff6fb80ef43   
[0x1]   mysqld!set_and_validate_user_attributes+0xc03   0xc9ce8f8130   0x7ff6fb80ae8f   
[0x2]   mysqld!mysql_create_user+0x7df   0xc9ce8f8940   0x7ff6fb607556   
[0x3]   mysqld!mysql_execute_command+0x1df6   0xc9ce8fcb00   0x7ff6fb60348f   
[0x4]   mysqld!dispatch_sql_command+0x36f   0xc9ce8fe000   0x7ff6fb6029e1   
[0x5]   mysqld!dispatch_command+0x19c1   0xc9ce8fe0b0   0x7ff6fb603860   
[0x6]   mysqld!do_command+0x230   0xc9ce8ff4a0   0x7ff6fb43c9f8   
[0x7]   mysqld!handle_connection+0x138   0xc9ce8ff940   0x7ff6fca6d7e9   
[0x8]   mysqld!pfs_spawn_thread+0x129   0xc9ce8ff980   0x7ff6fc4d3bdc   
[0x9]   mysqld!win_thread_start+0x1c   0xc9ce8ff9b0   0x7ffdba6c9363   
[0xa]   ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x93   0xc9ce8ff9e0   0x7ffdbbd9257d   
[0xb]   KERNEL32!BaseThreadInitThunk+0x1d   0xc9ce8ffa10   0x7ffdbcc2aa78   
[0xc]   ntdll!RtlUserThreadStart+0x28   0xc9ce8ffa40   0x0   

相关函数以插件形式保存在全局变量 mysqld!g_cached_authentication_plugins

img

MySQl 认证插件详解

MySQL 在启动时会初始化相关插件并加载插件的相关调用。每个身份认证插件都保存了一种认证方式,mysql在实现这些特性上有着和Windows类似的设计,结构体

MySQL 插件相关结构体

插件结构体,该结构体保存了插件的详细信息,包括句柄这么的,在mysql中,有几个重要的 st_mysql_plugin 结构体数组,builtin_mysql_password_pluginbuiltin_caching_sha2_password_plugin ,可以调试看看这两个结构体数组保存的相关内容。

struct st_mysql_plugin {
  int type;
  void *info; // 插件加密函数
  const char *name;
  const char *author;
  const char *descr;
  int license;
  int (*init)(MYSQL_PLUGIN);
  int (*check_uninstall)(MYSQL_PLUGIN);
  int (*deinit)(MYSQL_PLUGIN);
  unsigned int version;
  SHOW_VAR *status_vars;
  SYS_VAR **system_vars;
  void *__reserved1;
  unsigned long flags;
};

字段说明:

字段类型描述
typeint用于描述plugin的类型,随着版本更新,越来越多,在5.5中包含8种类型:MYSQL_UDF_PLUGIN,MYSQL_STORAGE_ENGINE_PLUGIN,MYSQL_FTPARSER_PLUGIN,MYSQL_DAEMON_PLUGIN,MYSQL_INFORMATION_SCHEMA_PLUGINMYSQL_AUDIT_PLUGINMYSQL_REPLICATION_PLUGINMYSQL_AUTHENTICATION_PLUGIN
infovoid*用于指向特定的plugin描述符结构体,在daemon plugin中结构体为st_mysql_daemon,一般第一个字段都是插件接口的版本号
nameconst char*plugin的名字,需要和安装时的名字一致
authorconst char*plugin的作者信息,会在i_s.plugins表中显示
descrconst char*描述插件
licenseubt插件许可证:PLUGIN_LICENSE_PROPRIETARYPLUGIN_LICENSE_GPLPLUGIN_LICENSE_BSD
initint (*init)(void *)当插件被加载时或者mysqld重启时会执行该函数,一般我们会在这里创建好后台线程
deinitint (*deinit)(void *);当插件被卸载时做的工作,例如取消线程,清理内存等
versionunsigned intplugin的版本信息
status_varsst_mysql_show_var*描述在执行show status时显示的信息
system_varsst_mysql_sys_var **描述在执行show variables显示的信息
__reserved1void*注释说为检查依赖而保留,不太明白,直接设为NULL即可
flagsunsigned long5.5之后增加的字段,plugin的flag:0、PLUGIN_OPT_NO_INSTALL(不可动态加载)、PLUGIN_OPT_NO_UNINSTALL(不可动态加载)

其中,info 成员比较重要,指向另外一个结构体 st_mysql_auth,先看一下 info 变量的结构体,保存了 mysql 身份认证插件相关的函数,st_mysql_plugin.info 指向的的结构体 st_mysql_auth:

// file: plugin_auth.h
/**
  Server authentication plugin descriptor
*/
struct st_mysql_auth {
  int interface_version; /** version plugin uses */
  /**
    A plugin that a client must use for authentication with this server
    plugin. Can be NULL to mean "any plugin".
  */
  const char *client_auth_plugin;
 
  authenticate_user_t authenticate_user;
  generate_authentication_string_t generate_authentication_string;
  validate_authentication_string_t validate_authentication_string;
  set_salt_t set_salt;
 
  /**
    Authentication plugin capabilities
  */
  const unsigned long authentication_flags;
 
  compare_password_with_hash_t compare_password_with_hash;
};

结构体相关变量

了解了相关结构之后,需要在mysql中查看相应的变量。查找相关结构体变量可以看到一个全局定义的宏

// file: sql_authentication.cc
 
mysql_declare_plugin(mysql_password){
    MYSQL_AUTHENTICATION_PLUGIN, /* type constant    */
    &native_password_handler,    /* type descriptor  */
    Cached_authentication_plugins::get_plugin_name(
        PLUGIN_MYSQL_NATIVE_PASSWORD), /* Name           */
    PLUGIN_AUTHOR_ORACLE,              /* Author           */
    "Native MySQL authentication",     /* Description      */
    PLUGIN_LICENSE_GPL,                /* License          */
    nullptr,                           /* Init function    */
    nullptr,                           /* Check uninstall  */
    nullptr,                           /* Deinit function  */
    0x0101,                            /* Version (1.0)    */
    nullptr,                           /* status variables */
    nullptr,                           /* system variables */
    nullptr,                           /* config options   */
    0,                                 /* flags            */
},
{
    MYSQL_AUTHENTICATION_PLUGIN, /* type constant    */
	&sha256_password_handler,    /* type descriptor  */
    Cached_authentication_plugins::get_plugin_name(
        PLUGIN_SHA256_PASSWORD),      /* Name             */
    PLUGIN_AUTHOR_ORACLE,             /* Author           */
    "SHA256 password authentication", /* Description      */
    PLUGIN_LICENSE_GPL,               /* License          */
    &init_sha256_password_handler,    /* Init function    */
    nullptr,                          /* Check uninstall  */
    nullptr,                          /* Deinit function  */
    0x0101,                           /* Version (1.0)    */
    nullptr,                          /* status variables */
    sha256_password_sysvars,          /* system variables */
    nullptr,                          /* config options   */
    0                                 /* flags            */
} mysql_declare_plugin_end;

展开后如下所示, 也就能看到对应的结构体 st_mysql_plugin 以及其数组定义了

int builtin_mysql_password_plugin_interface_version = 0x010B; 
int builtin_mysql_password_sizeof_struct_st_plugin = sizeof(struct st_mysql_plugin); 
struct st_mysql_plugin builtin_mysql_password_plugin[] = {
    {
        MYSQL_AUTHENTICATION_PLUGIN, /* type constant    */
        &native_password_handler,    /* type descriptor  */
        Cached_authentication_plugins::get_plugin_name(
        PLUGIN_MYSQL_NATIVE_PASSWORD), /* Name           */
        PLUGIN_AUTHOR_ORACLE,              /* Author           */
        "Native MySQL authentication",     /* Description      */
        PLUGIN_LICENSE_GPL,                /* License          */
        nullptr,                           /* Init function    */
        nullptr,                           /* Check uninstall  */
        nullptr,                           /* Deinit function  */
        0x0101,                            /* Version (1.0)    */
        nullptr,                           /* status variables */
        nullptr,                           /* system variables */
        nullptr,                           /* config options   */
        0,                                 /* flags            */
    },
    {
        MYSQL_AUTHENTICATION_PLUGIN, /* type constant    */
        &sha256_password_handler,    /* type descriptor  */
        Cached_authentication_plugins::get_plugin_name(
            PLUGIN_SHA256_PASSWORD),      /* Name             */
        PLUGIN_AUTHOR_ORACLE,             /* Author           */
        "SHA256 password authentication", /* Description      */
        PLUGIN_LICENSE_GPL,               /* License          */
        &init_sha256_password_handler,    /* Init function    */
        nullptr,                          /* Check uninstall  */
        nullptr,                          /* Deinit function  */
        0x0101,                           /* Version (1.0)    */
        nullptr,                          /* status variables */
        sha256_password_sysvars,          /* system variables */
        nullptr,                          /* config options   */
        0                                 /* flags            */
    }, 
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } 
}

还有 caching_sha2_password 身份认证插件对应的宏定义,展开后与 mysql_password 类似,这里就省略了。

// file: sha2_password.cc
/*
  caching_sha2_password plugin declaration
*/
 
mysql_declare_plugin(caching_sha2_password){
    MYSQL_AUTHENTICATION_PLUGIN, /* plugin type                   */
    &caching_sha2_auth_handler,  /* type specific descriptor      */
    Cached_authentication_plugins::get_plugin_name(
        PLUGIN_CACHING_SHA2_PASSWORD),      /* plugin name          */
    PLUGIN_AUTHOR_ORACLE,                   /* author                        */
    "Caching sha2 authentication",          /* description                   */
    PLUGIN_LICENSE_GPL,                     /* license                       */
    caching_sha2_authentication_init,       /* plugin initializer            */
    nullptr,                                /* Uninstall notifier            */
    caching_sha2_authentication_deinit,     /* plugin deinitializer          */
    0x0100,                                 /* version (1.0)                 */
    caching_sha2_password_status_variables, /* status variables              */
    caching_sha2_password_sysvars,          /* system variables              */
    nullptr,                                /* reserved                      */
    0,                                      /* flags                         */
},
    {
        MYSQL_AUDIT_PLUGIN,   /* plugin type                   */
        &sha2_cache_cleaner,  /* type specific descriptor      */
        "sha2_cache_cleaner", /* plugin name                   */
        PLUGIN_AUTHOR_ORACLE, /* author                        */
        "Cache cleaner for Caching sha2 authentication", /* description */
        PLUGIN_LICENSE_GPL,                /* license                       */
        caching_sha2_cache_cleaner_init,   /* plugin initializer            */
        nullptr,                           /* Uninstall notifier            */
        caching_sha2_cache_cleaner_deinit, /* plugin deinitializer          */
        0x0100,                            /* version (1.0)                 */
        nullptr,                           /* status variables              */
        nullptr,                           /* system variables              */
        nullptr,                           /* reserved                      */
        0                                  /* flags                         */
    } mysql_declare_plugin_end;

以及相应的全局成员变量(file:sql_builtin.cc.in)如下所示:

//sql_builtin.cc.in
 
#include "mysql/plugin.h"
 
typedef struct st_mysql_plugin builtin_plugin[];
 
#ifdef _MSC_VER
extern "C"
#else
extern
#endif
builtin_plugin 
  @mysql_mandatory_plugins@ @mysql_optional_plugins@ builtin_binlog_plugin, builtin_mysql_password_plugin, builtin_caching_sha2_password_plugin, builtin_daemon_keyring_proxy_plugin;
 
struct st_mysql_plugin *mysql_optional_plugins[]=
{
  @mysql_optional_plugins@ 0
};
 
struct st_mysql_plugin *mysql_mandatory_plugins[]=
{
  builtin_binlog_plugin, builtin_mysql_password_plugin, builtin_caching_sha2_password_plugin, builtin_daemon_keyring_proxy_plugin, @mysql_mandatory_plugins@ 0
};

IDA 查看可以发现,mysqld 导出了一个 st_mysql_plugin 的变量 builtin_mysql_password_plugin

image-20231208105555983

上述两个结构体只是 mysql_native_passwordsha356_password 两个的相关句柄,caching_sha2_auth_handler 的相关处理句柄在另外一个变量中,如下所示:

/** st_mysql_auth for caching_sha2_password plugin */
static struct st_mysql_auth caching_sha2_auth_handler {
  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
      Cached_authentication_plugins::get_plugin_name(
          PLUGIN_CACHING_SHA2_PASSWORD),
      caching_sha2_password_authenticate, caching_sha2_password_generate,
      caching_sha2_password_validate, caching_sha2_password_salt,
      AUTH_FLAG_USES_INTERNAL_STORAGE, compare_caching_sha2_password_with_hash
};

生成密码函数

生成密码的函数原型,也就是 st_mysql_auth 中的 generate_authentication_string_t 成员。

对应 generate_native_passwordsha256_password_authenticate 以及 caching_sha2_password_generate

/**
  New plugin API to generate password digest out of authentication string.
  This function will first invoke a service to check for validity of the
  password based on the policies defined and then generate encrypted hash
 
  @param[out]   outbuf      A buffer provided by server which will hold the
                            authentication string generated by plugin.
  @param[in,out] outbuflen   Length of server provided buffer as IN param and
                            length of plugin generated string as OUT param.
  @param[in]    inbuf       auth string provided by user.
  @param[in]    inbuflen    auth string length.
 
  @retval  0 OK
  @retval  1 ERROR
*/
typedef int (*generate_authentication_string_t)(char *outbuf,
                                                unsigned int *outbuflen,
                                                const char *inbuf,
                                                unsigned int inbuflen);

caching_sha2_password 为例,在相关函数 caching_sha2_password_generate 打断点调试, 可以看到初始密码为 admin123 经过加密之后加密字符串

$A$005$"4eh#.@.^B3Tu.[ngw'.81iezRRMhYZg1f.29kTTLOKMTbmQWXSZfUJ6NF/4a.8
image-20231208113526976

查一下数据库入库密码

image-20231208114646191