Jinja2 SSTI - Filter Bypass help needed

So, I am in a CTF where I need to exploit a server vulnerable to jinja2 SSTI.

The challenges are:

  • Full attack, including {{ and }} needs to be 45 characters or less
  • The system rejects any strings which contain ", (,),[ and ] (possibly more, that's all I've confirmed so far).
  • I can use ' and { but when I replace () with {} it generates a HTTP 500 error.

I have got most of the way, but now need to try and get an arbitrary file read (ideally using os.popen) but I've hit a block.

Can anyone suggest ways I could bypass the restrictions on () (and URL encoding, Hex Encoding etc hasn't worked).

self.__dict__ has returned a lot of data but, so far, I've failed at making it useful. I've tried a few tutorials but they all require () to work.

Has anyone got any suggestions?

TazWake

Note: https://www.nohello.com/

Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

Currently have very limited HTB time but will try to respond as quickly as possible.

Tagged:
«1

Comments

  • edited November 2020

    Some good news, it also blocks \ and .

    Urgh.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • And it blocks +. This is killing me :sweat_smile:

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • Unfortunately no - at least not in any way I can get it to accept. It looks like there is a validation check first which is pretty aggressive on decoding stuff.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • :P helps to fully read before trying to post. Done so much sqli the past couple weeks lol everything is sql

  • Its ok @PrivacyMonk3y - I really appreciate the help!

    I've spend hours on this step this evening.

    I think I've gone word blind. I don't want to stop because it took me ages to get to this point and I don't have a way to leap back here when I spin it up again :smile:

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • lmfao that sucks been there :)

    Gl hopefully something breaks soon :D

  • Have you tried using alternative parentheses? Like e.g. the UTF-8 full-width characters? Maybe the filter is somewhere in front and Jinja gracefully converts them back, for you.

    os.system('id')
    aka. os.system%uff08'id'%uff09


    Hack The Box
    GREM | OSCE | GASF | eJPT

  • @HomeSen said:

    Have you tried using alternative parentheses? Like e.g. the UTF-8 full-width characters? Maybe the filter is somewhere in front and Jinja gracefully converts them back, for you.

    os.system('id')
    aka. os.system%uff08'id'%uff09

    I hadn't - trying now.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • edited November 2020

    @HomeSen said:

    Have you tried using alternative parentheses? Like e.g. the UTF-8 full-width characters? Maybe the filter is somewhere in front and Jinja gracefully converts them back, for you.

    os.system('id')
    aka. os.system%uff08'id'%uff09

    I owe you at least a small beer!

    It progressed slightly - getting server errors now but that could be down to all the crap I've been throwing at it.

    So far it looks like this bypassed at least part of the content filtering.

    EDITED TO ADD
    Might have been a bit too optimistic. It just generates HTTP500s even with a clean boot, I think its breaking the content filter rather than bypass. Also it s a lot of characters when I only have 45 to play with.

    But I am genuinely indebted to @HomeSen for the nudge here.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • Too bad.
    Just checked how Chrome would URL-encode those. Maybe try %EF%BC%88 and %EF%BC%89 for the opening and closing parentheses.


    Hack The Box
    GREM | OSCE | GASF | eJPT

  • Sadly that one also triggers the content filter as it decodes it quickly.

    I'd never thought of using that before though, so I am going to add those to my notebooks! Thanks!

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • Might be a silly suggestion, but can you use Jinja2's |safe filter to disable the automatic escaping?

  • @svenkali said:

    Might be a silly suggestion, but can you use Jinja2's |safe filter to disable the automatic escaping?

    Sadly this isn't working either. As far as I can establish, the content filtering is happening before the code is sent to Jinja2.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • Bump - I am still stuck :smile: :cry:

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • Type your comment> @TazWake said:

    Bump - I am still stuck :smile: :cry:

    Can you send me challenge URL ? It's seem fun, I want to give it a try :blush:

    jkana101
    OSCP | Sec+ | MCSE | VCP | CCNA

  • @jkana101 said:

    Can you send me challenge URL ? It's seem fun, I want to give it a try :blush:

    It isn't a publicly accessible challenge, sorry. Its provided by a commercial enterprise to a client who given me access.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • So, I am still stuck, but I've made some progress.

    I can write to the HTTP referrer string and I can call request.environ.HTTP_REFERER in the SSTI, but it still remains static.

    So for example: {{os.popen.request.environ.HTTP_REFERER}} doesn't work but {{request.environ.HTTP_REFERER}} does actually print whatever I put in the referer field.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • edited November 2020

    Oh my god... Taz asking for help?

    So for example: {{os.popen.request.environ.HTTP_REFERER}} doesn't work but {{request.environ.HTTP_REFERER}} does actually print whatever I put in the referer field.

    So clearly the OS module is not wholly imported, just os.popen.request. Maybe something useful in there? Not 100% sure though without being able to look at it.

    Then again that has absolutely nothing to do with the HTTP_REFERER string unless you are down a rabbit hole, it does seem like a perfect XSS opportunity only question is how to trigger it...

    LMAY75
    Always happy to help, DM me if you need anything!
    Link to Profile

  • It could well be a rabbit hole. Right now I have no way to tell.

    However it is a lab on Jinja2 exploitation so I think XSS isn't really part of the lab builders plans.

    I have space for 41 characters between the {{ and }} markers, so my options are limited.

    I'd like to do an os.popen('id').read() at the very least but ( and ) are blocked so that doesn't work. Sending a referer with variations of the (id).read() string didn't work.
    Using __getchilditem__ syntax makes some progress but quickly runs out of space.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • Type your comment> @TazWake said:

    It could well be a rabbit hole. Right now I have no way to tell.

    However it is a lab on Jinja2 exploitation so I think XSS isn't really part of the lab builders plans.

    SSTI is a subset of XSS

    As for the rest, the only bracket bypass I can think of uses parenthesis... somehow the backend must be using them or else would not be able to run. That means there is prefiltering prior to hitting the template... have you looked into py modules that do this?

    LMAY75
    Always happy to help, DM me if you need anything!
    Link to Profile

  • Yeah - the content filtering happens before the data is sent through to the Jinja2 part. As far as I can tell its a custom set of filtering and it creates all kinds of issues.

    For example, any attempt to submit a request with (),[],%, ,+, \, " (and a few other symbols) is rejected before it gets to the engine. It also rejects various encodings to try and sneak them past.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • Bump - just in case anyone has any good ideas.

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • I figure you would have told us already if you knew (mainly a bump excuse), but were you able to narrow down what the filter is likely built on? Is it getting filtered in the web app, a modification to the backend server running Jinja2, python, etc.?

  • The filter is the first stage of the application, the exploit path is built on an authentication form. If you create a user who already exists, the subsequent messages are passed through the app to an SSTI-vulnerable application.

    For example, if you create the first user as {{'7'*7}}@example.com, nothing happens. If you then resend a user creation request with the same email, you get a message saying [email protected] already exists.

    What this means is that the first creation has to work, which means it has to get through the filtering in place on that stage - this largely excludes anything I've tried :cry: :smile: .

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • I've made some progress here.

    Turn out if I use "{{ ...... }}"@a.bc I can now use () and other characters in the SSTI string.

    It does mean I lose another two characters in the payload though. Now I need a 39 character attack :smile:

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • Sadly, I still cant get anything to work! :smile: :cry: :scream:

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

  • Whoever created that challenge needs a hug, or a puppy, or both.

  • edited December 2020

    Type your comment> @TazWake said:

    Sadly, I still cant get anything to work! :smile: :cry: :scream:

    So I don't know too much about the topic, but maybe some brainstorming triggers an idea.
    So from what I know, character limits don't go well with filter bypass. So what came to my mind is to declare variables and use them in another payload.

    Something like:
    {{asd=config.base___}}@ab.cd
    {{asd.__subclasses
    ()}}@ab.cd

    So this way you solve the problem of the character limit.

    PD: I just run a htb machine with jinja2 to try this, and looks like it could work.

    EDIT: the syntax to define a variable is {% set test = 'DeepPurple' %}

  • @gamedeth said:

    So I don't know too much about the topic, but maybe some brainstorming triggers an idea.
    So from what I know, character limits don't go well with filter bypass. So what came to my mind is to declare variables and use them in another payload.

    Something like:
    {{asd=config.base___}}@ab.cd
    {{asd.__subclasses
    ()}}@ab.cd

    So this way you solve the problem of the character limit.

    PD: I just run a htb machine with jinja2 to try this, and looks like it could work.

    EDIT: the syntax to define a variable is {% set test = 'DeepPurple' %}

    Thats pretty genius! I hadn't thought about that at all. I wont get a chance to try it until next week but its given me some new ideas and new hope! Thanks!

    TazWake

    Note: https://www.nohello.com/

    Happy to help people but PLEASE explain your problem in as much detail as possible! If you say vague things like "It's not working", I cant help. This isn't Twitter so my DMs are always open.

    Currently have very limited HTB time but will try to respond as quickly as possible.

Sign In to comment.