MobaXterm 是非常优秀的shell 工具,使用过程中发现个人版无法查看历史保存的密码,故百度了以下 并将可行的方案整理如下:
reg query HKEY_CURRENT_USER\Software\Mobatek\MobaXterm\C # Credentials
reg query HKEY_CURRENT_USER\Software\Mobatek\MobaXterm\P # Passwords
可以看到密码被编码成了新的字符串。在运行解码代码之前,我们还需要更新下Python环境。
pip uninstall pycrypto
pip install pycryptodome
这里我们借助脚本,避免重复造轮子
点击查看代码 ShowMobaXterm.py
#!/usr/bin/env python3
import sys, os, platform, random, base64, itertools, winreg
from Crypto.Hash import SHA512
from Crypto.Cipher import AES
if platform.system().lower() != 'windows':
print('Please run this script in Windows.')
exit(-1)
class MobaXtermCrypto:
def __init__(self, SysHostname: bytes, SysUsername: bytes, SessionP: bytes = None):
self._SysHostname = SysHostname
self._SysUsername = SysUsername
self._SessionP = SessionP
def _KeyCrafter(self, **kargs) -> bytes:
if kargs.get('ConnHostname') != None and kargs.get('ConnUsername') != None:
s1 = self._SysUsername + self._SysHostname
while len(s1) < 20:
s1 = s1 + s1
s2 = kargs.get('ConnUsername') + kargs.get('ConnHostname')
while len(s2) < 20:
s2 = s2 + s2
key_space = [
s1.upper(),
s2.upper(),
s1.lower(),
s2.lower()
]
else:
s = self._SessionP
while len(s) < 20:
s = s + s
key_space = [
s.upper(),
s.upper(),
s.lower(),
s.lower()
]
key = bytearray(b'0d5e9n1348/U2+67')
for i in range(0, len(key)):
b = key_space[(i + 1) % len(key_space)][i]
if (b not in key) and (b in b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/'):
key[i] = b
return bytes(key)
def EncryptPassword(self, Plaintext: bytes, ConnHostname: bytes, ConnUsername: bytes) -> str:
key = self._KeyCrafter(ConnHostname = ConnHostname, ConnUsername = ConnUsername)
ct = bytearray()
for char in Plaintext:
l = char & 0x0f
ct.append(key[l])
key = key[-1:] + key[0:-1]
h = char >> 4
ct.append(key[h])
key = key[-1:] + key[0:-1]
Ciphertext = bytearray()
obfuscate_chars = bytes(filter(lambda char: char not in key, b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/'))
for char in ct:
while random.choice([ True, False, False ]) == False:
Ciphertext.append(random.choice(obfuscate_chars))
Ciphertext.append(char)
while random.choice([ True, False, False ]) == False:
Ciphertext.append(random.choice(obfuscate_chars))
return Ciphertext.decode()
def EncryptCredential(self, Plaintext: bytes) -> str:
key = self._KeyCrafter()
ct = bytearray()
for char in Plaintext:
l = char & 0x0f
ct.append(key[l])
key = key[-1:] + key[0:-1]
h = char >> 4
ct.append(key[h])
key = key[-1:] + key[0:-1]
Ciphertext = bytearray()
obfuscate_chars = bytes(filter(lambda char: char not in key, b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/'))
for char in ct:
while random.choice([ True, False, False ]) == False:
Ciphertext.append(random.choice(obfuscate_chars))
Ciphertext.append(char)
while random.choice([ True, False, False ]) == False:
Ciphertext.append(random.choice(obfuscate_chars))
return Ciphertext.decode()
def DecryptPassword(self, Ciphertext: str, ConnHostname: bytes, ConnUsername: bytes) -> bytes:
key = self._KeyCrafter(ConnHostname = ConnHostname, ConnUsername = ConnUsername)
ct = bytearray()
for char in Ciphertext.encode('ascii'):
if char in key:
ct.append(char)
if len(ct) % 2 == 0:
pt = bytearray()
for i in range(0, len(ct), 2):
l = key.find(ct[i])
key = key[-1:] + key[0:-1]
h = key.find(ct[i + 1])
key = key[-1:] + key[0:-1]
assert(l != -1 and h != -1)
pt.append(16 * h + l)
return bytes(pt)
else:
raise ValueError('Invalid ciphertext.')
def DecryptCredential(self, Ciphertext: str) -> bytes:
key = self._KeyCrafter()
ct = bytearray()
for char in Ciphertext.encode('ascii'):
if char in key:
ct.append(char)
if len(ct) % 2 == 0:
pt = bytearray()
for i in range(0, len(ct), 2):
l = key.find(ct[i])
key = key[-1:] + key[0:-1]
h = key.find(ct[i + 1])
key = key[-1:] + key[0:-1]
assert (l != -1 and h != -1)
pt.append(16 * h + l)
return bytes(pt)
else:
raise ValueError('Invalid ciphertext.')
class MobaXtermCryptoSafe:
def __init__(self, MasterPassword: bytes):
self._Key = SHA512.new(MasterPassword).digest()[0:32]
def EncryptPassword(self, Plaintext: bytes) -> str:
iv = AES.new(key = self._Key, mode = AES.MODE_ECB).encrypt(b'\x00' * AES.block_size)
cipher = AES.new(key = self._Key, iv = iv, mode = AES.MODE_CFB, segment_size = 8)
return base64.b64encode(cipher.encrypt(Plaintext))
def EncryptCredential(self, Plaintext: bytes) -> str:
return self.EncryptPassword(Plaintext)
def DecryptPassword(self, Ciphertext: str) -> bytes:
iv = AES.new(key = self._Key, mode = AES.MODE_ECB).encrypt(b'\x00' * AES.block_size)
cipher = AES.new(key = self._Key, iv = iv, mode = AES.MODE_CFB, segment_size = 8)
return cipher.decrypt(base64.b64decode(Ciphertext))
def DecryptCredential(self, Ciphertext: str) -> bytes:
return self.DecryptPassword(Ciphertext)
def WinRegistryReadValue(Root, SubKey: str, ValueName: str, ExpectValueType: int = None):
Key = winreg.OpenKey(Root, SubKey)
Value, ValueType = winreg.QueryValueEx(Key, ValueName)
if type(ExpectValueType) == int and ValueType != ExpectValueType:
raise TypeError('Expect %d, but %d is given.' % (ExpectValueType, ValueType))
return Value
if len(sys.argv) == 2:
cipher = MobaXtermCryptoSafe(
sys.argv[1].encode('ansi')
)
else:
Value, ValueType = winreg.QueryValueEx(
winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
'Software\\Mobatek\\MobaXterm'
),
'SessionP'
); assert(ValueType == winreg.REG_SZ)
cipher = MobaXtermCrypto(
platform.node().encode('ansi'),
os.getlogin().encode('ansi'),
Value.encode('ansi')
)
try:
Key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\\Mobatek\\MobaXterm\\C')
print('Credentials'.center(48, '-'))
for i in itertools.count(0):
try:
ValueName, Value, ValueType = winreg.EnumValue(Key, i)
assert(ValueType == winreg.REG_SZ)
CredentialUsername, CredentialPassword = Value.split(':')
CredentialPassword = cipher.DecryptCredential(
CredentialPassword
).decode('ansi')
print('[*] Name: %s' % ValueName)
print('[*] Username: %s' % CredentialUsername)
print('[*] Password: %s' % CredentialPassword)
print('')
except OSError:
break
except FileNotFoundError:
pass
try:
Key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\\Mobatek\\MobaXterm\\P')
print('Passwords'.center(48, '-'))
for i in itertools.count(0):
try:
ValueName, Value, ValueType = winreg.EnumValue(Key, i)
assert(ValueType == winreg.REG_SZ)
ConnUsername, ConnHostname = ValueName.split('@')
if ':' in ConnUsername:
ConnUsername = ConnUsername.split(':')[-1]
ConnPassword = cipher.DecryptPassword(
Value,
#ConnHostname.encode('ansi'),
#ConnUsername.encode('ansi')
).decode('ansi')
print('[*] Name: %s' % ValueName)
print('[*] Password: %s' % ConnPassword)
print('')
except OSError:
break
except FileNotFoundError:
pass
单条密码
下面,我们就可以运行对应的文件进行密码解码了。我们随机挑选一个session,以第一个为例,取最后一列中的字符串W8jczTkOZRzGXWnc(编码后的密码),使用如下命令,即可解码出原始的密码!
python MobaXtermCipher.py dec -p Master密码 W8jczTkOZRzGXWnc
(PS:这里需要区分是否有本地的Master密码,要使用不同的参数,上述为配置了Master密码的情况,具体的可以查看github项目中的Readme)
全部密码
如果我们想看到全部Session的密码,还可以使用项目中的另外一个文件ShowMobaXterm.py。
在作者的尝试过程中,发现也需要修改代码,可能也是和版本相关,大家在使用时可以根据环境情况决定是否要修改。具体的修改内容为注释掉代码中的230、231两行。
ConnPassword = cipher.DecryptPassword(
Value,
# ConnHostname.encode('ansi'),
# ConnUsername.encode('ansi')
).decode('ansi')
然后,执行该文件即可得到全部Session的密码,这里同样用到了Master密码(MobaXterm 启动时提示你输入的密码)。
python ShowMobaXterm.py Master密码
输出如下