Security issues with web applications

So far, we have seen four types of security issues with Python, namely, those with reading input, evaluating expressions, overflow errors, and serialization issues. All our examples so far have been with Python on the console.

However, almost all of us interact with web applications on a daily basis, many of which are written in Python web frameworks such as Django, Flask, Pyramid, and others. Hence, it is more likely that we are exposed to security issues in such applications. We will look at a few examples here.

Server Side Template Injection

Server Side Template Injection (SSTI) is an attack using the server-side templates of common web frameworks as an attack vector. The attack uses weaknesses in the way user input is embedded on the templates. SSTI attacks can be used to figure out internals of a web application, execute shell commands, and even fully compromise the servers.

We will see an example using a very popular web application framework in Python, namely, Flask.

The following is the sample code for a rather simple web application in Flask with an inline template:

# ssti-example.py
from flask import Flask
from flask import request, render_template_string, render_template

app = Flask(__name__)

@app.route('/hello-ssti')
defhello_ssti():
    person = {'name':"world", 'secret': 'jo5gmvlligcZ5YZGenWnGcol8JnwhWZd2lJZYo=='}
    if request.args.get('name'):
        person['name'] = request.args.get('name')

    template = '<h2>Hello %s!</h2>' % person['name']
    return render_template_string(template, person=person)

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

Running it on the console, and opening it in the browser allows us to play around with the hello-ssti route:

$ python3 ssti_example.py 
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger pin code: 163-936-023

First, let's try some benign inputs:

Server Side Template Injection

Here is another example.

Server Side Template Injection

Next, let's try with some crafty inputs which an attacker may use.

Server Side Template Injection

What is happening here?

Since the template uses unsafe %s string templates, it evaluates anything that is passed to it into Python expressions. We passed {{ person.secret }}, which, in the Flask templating language (Flask uses Jinja2 templating), got evaluated to the value of the key secret in the dictionary person, effectively exposing the secret key of the app!

We can perform even more ambitious attacks, as this hole in the code allows an attacker to try the full power of Jinja templates, including for loops. Here is an example:

Server Side Template Injection

The URL used for the attack is as follows:

http://localhost:5000/hello-ssti?name={% for item in person %}<p>{{ item, person[item] }}</p>{% endfor %}

This goes through a for loop, and tries to print all contents of the person dictionary.

This also allows an attacker easy access to the sensitive server-side configuration parameters. For example, he can print out the Flask configuration by passing the name parameter as {{ config }}.

Here is the screenshot of the browser, printing the server configuration using this attack.

Server Side Template Injection

Server-Side Template Injection – Mitigation

We saw in the previous section some examples of using server side templates as an attack vector to expose sensitive information of the web application/server. In this section, we will see how the programmer can safeguard his code against such attacks.

In this specific case, the fix for this is to use the specific variable that we want in the template, rather than the dangerous, allow-all %s string. Here is the modified code with the fix:

# ssti-example-fixed.py
from flask import Flask
from flask import request, render_template_string, render_template

app = Flask(__name__)

@app.route('/hello-ssti')
defhello_ssti():
    person = {'name':"world", 'secret': 
jo5gmvlligcZ5YZGenWnGcol8JnwhWZd2lJZYo=='}
    if request.args.get('name'):
        person['name'] = request.args.get('name')

    template = '<h2>Hello {{ person.name }} !</h2>'
    return render_template_string(template, person=person)

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

Now the earlier attacks all fizzle off.

Here is the browser screenshot for the first attack:

Server-Side Template Injection – Mitigation

Here is the browser screenshot for the next attack.

Server-Side Template Injection – Mitigation

Denial of Service

Now let's look at another attack that is commonly used by malicious hackers, namely, Denial of Service (DoS).

DoS attacks target vulnerable routes or URLs in a web application, and sends them crafty packets or URLs, which either force the server to perform infinite loops or CPU-intensive computations, or force it to load huge amounts of data from databases, which puts a lot of load on the server CPU, preventing the server from executing other requests.

Note

A DDoS or distributed DoS attack is when the DoS attack is performed in a choreographed way using multiple systems targeting a single domain. Usually thousands of IP addresses are used, which are managed via botnets.

We will see a minimal example of a DoS attack using a variation of our previous example:

# ssti-example-dos.py
from flask import Flask
from flask import request, render_template_string, render_template

app = Flask(__name__)

TEMPLATE = '''
<html>
 <head><title> Hello {{ person.name }} </title></head>
 <body> Hello FOO </body>
</html>
'''

@app.route('/hello-ssti')
defhello_ssti():
    person = {'name':"world", 'secret': 'jo5gmvlligcZ5YZGenWnGcol8JnwhWZd2lJZYo=='} 
    if request.args.get('name'):
        person['name'] = request.args.get('name')

    # Replace FOO with person's name
    template = TEMPLATE.replace("FOO", person['name'])
    return render_template_string(template, person=person)

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

In the preceding code, we use a global template variable named TEMPLATE, and use the safer {{ person.name }} template variable as the one used with the SSTI fix. However, the additional code here is a replacement of the holding name FOO with the name value.

This version has all the vulnerabilities of the original code, even with the %s code removed. For example, take a look at the following screenshot of the browser exposing the {{ person.secret }} variable value in the body, but not in the title of the page.

Denial of Service

This is due to this following line of code that we added shown as follows:

 # Replace FOO with person's name
 template = TEMPLATE.replace("FOO", person['name'])

Any expression passed is evaluated, including the arithmetic ones. For example:

Denial of Service

This opens up pathways to simple DoS attacks by passing in CPU-intensive computations that the server cannot handle. For example, in the following attack, we pass in a very large computation of a number, which occupies the CPU of the system, slows the system down and makes the application non-responsive:

Denial of Service

An example demonstrating a DoS style attack using computationally intensive code.

is attack is http://localhost:5000/hello-ssti?name=Tom {{ 100*100000000 }}.

By passing in the arithmetical expression {{ 100**100000000 }}, which is computationally intensive, the server is overloaded and cannot handle other requests.

As you can see in the previous screenshot, the request never completes, and also prevents the server from responding to other requests; as you can see from how a normal request to the same application on a new tab opened on the right side is also held up causing the effect of a DoS style attack:

Denial of Service

A new tab opened on the right side of the tab with attack vector shows that the application has become unresponsive

Cross-Site Scripting (XSS)

The code that we used in the earlier section to demonstrate a minimalistic DOS attack is also vulnerable to script injection. Here is an illustration:

Cross-Site Scripting (XSS)

A simple demonstration of XSS scripting using server side templates and JavaScript injection

The URL used for this attack is as follows:

http://localhost:5000/hello-ssti?name=Tom<script>alert("You are under attack!")</script>

These kinds of script injection vulnerabilities can lead to XSS, a common form of web exploit where attackers are able to inject malicious scripts into your server's code, which are loaded from other websites, and take control of it.

Mitigation – DoS and XSS

We saw a few examples of DoS attacks and simple XSS attacks in the previous section. Now let's look at how the programmer can take steps in his code to mitigate such attacks.

In the previous specific example that we have used for illustration, the fix is to remove the line that replaces the string FOO with the name value, and to replace it with the parameter template itself. For good measure, we also make sure that the output is properly escaped by using the escape filter, |e, of Jinja 2. Here is the rewritten code:

# ssti-example-dos-fix.py
from flask import Flask
from flask import request, render_template_string, render_template

app = Flask(__name__)

TEMPLATE = '''
<html>
 <head><title> Hello {{ person.name | e }} </title></head>
 <body> Hello {{ person.name | e }} </body>
</html>
'''

@app.route('/hello-ssti')
defhello_ssti():
    person = {'name':"world", 'secret': 'jo5gmvlligcZ5YZGenWnGcol8JnwhWZd2lJZYo=='} 
    if request.args.get('name'):
        person['name'] = request.args.get('name')
    return render_template_string(TEMPLATE, person=person)

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

Now that both of the vulnerabilities are mitigated, the attacks have no effect, and fail harmlessly.

Here is an screenshot demonstrating the DoS attack .

Mitigation – DoS and XSS

Here is the one, demonstrating the XSS attack.

Mitigation – DoS and XSS

Similar vulnerabilities due to bad code in server side templates exist in other Python web frameworks such as Django, Pyramid, Tornado, and others. However, a step-by-step discussion on each of these is beyond the scope of this chapter. The interested reader is directed to security resources on the web discussing such issues.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.75.165