Machine Problem 3
Created: April 30, 2024 at 13:41
Last Modified: April 30, 2024 at 13:41
Authors: Joe MamaWeb Application Vulnerabilities
TL;DR
Attacks:
- SQL Injection (login, sesion token)
- Stored XSS attack
- CSRF Attack (form)
Fixes:
- Query Parameterization
- Sanitization (replace ‘<’:’<’, ‘>’:’>’)
- CSRF Token
- Rate Limiting (disallow brute force)
click me for repo link to patched-app
Attacks
Just the tip (basic login SQL injection)
After starting up the service, we are greeted with a simple login page. One look at this login page and you can tell that this website’s TOTALLY asking for it. Look at that sweet login form. It needs to be broken in with some Big Bad Code (injection). So lets put just the tip in first:
|
|
Recalling our SQL Injection
lecture that for vulnerable websites that naively fetch a single user using direct-input query, we can force a login by making the query return at least one entry.

We click login:

Now that we’re here, lets do a little bit of trolling.
XSS Attack!
We can try a nifty little exploit to test if our inputs are being sanitized:
|
|
We hit post, and:

We can also do something a little more evil, like
|
|
which effectively sends a valid GET
request to the logout endpoint. After clicking post:

Immediately getting kicked out of the site is kind of annoying though, and we dont really have any way of deleting posts, so lets just wipe those from the database directly.
CSRF Attack!
We also can do a little bit of trolling from the outside, like a CSRF attack, like so:
|
|
which looks like this:
Executing the attack:

Session Token SQL Injection
Since there was not much else we could do, taking a look at the code reveals a vulnerable session token query - which we can exploit. First, we must make sure that the session token in the database is not deleted, which means we cannot send a GET
request to the logout
endpoint.
What we do is delete the session_token
cookie stored in our browser and reload the site - this way we get logged out without having the session_token
stored in the database deleted.
In a realistic scenario, we can just assume that we are trying to login to an account of a user that’s current logged in. Anyway, lets do that attack now - similar to our basic SQL injection
payload, we craft a session_token
cookie in our browser:

We reload this to send the cookie, and just like that:

Fixes
????????????
Literally ?
or parameterization saves us from SQL
injection, like so:
|
|
We apply this to every SQL query made. The fully patched code is located here
Bleach
Literally just swap out croccy symbols. We use a helper function sanitize
like so:
|
|

Not today sir, CSRF only
We can simply implement a CSRF Token to disable these kinds of attacks like so:
|
|
Following the advise of our wise elders, we generate a CSRF token everytime our user fetches a form, and we send it as a cookie to them and also place it as a hidden input in our form code:
|
|
and of course we check for the CSRF token when a POST
request is made to the /posts
route:
|
|
We didn’t really change much of the code in the app after this, so a valid POST request is made resulting in a 302 FOUND
response, but it doesn’t get past the csrf_token
check so nothing is done beyond that.

What else can we do
Alright, we fixed the major security flaws. But to make this MP more complicated interesting, we can also implement a simple rate limiter to prevent brute force attacks.
For the sake of not rewheeling the invention, we can just install the Flask Limiter extension. The simplest setup (yoinked from the documentation) is enough for this MP.
We setup the limiter like this
|
|
We can now limit the access to our endpoints.
For the login
endpoint, we can use multiple rate limits for funsies.
|
|
For the posts
endpoint, we can do something like this
|
|
When a user reaches the rate limit, they will get these errors and won’t be able to make requests until some time has passed.



To make this even more interesting, we can check if this actually works using other devices. Unfortunately, using flask run
won’t allow other devices to connect to our server, even if we expose port 5000 in our Firewall. Although this is not recommended, we can just simply run flask with flask run --host=0.0.0.0
while still exposing port 5000 in our Firewall.
I’m using my phone to test this.

Emploice Muswashans

So what have we learned?
We learned to clean our inputs. And, well, use CSRF tokens.
Most of the fixes are just sanitization with extra steps.
Don’t be like 2014 TweetDeck that forgot to do this.
Or that one University portal that still doesn’t do this (yes, we’re all looking at you).
Or that other University that just got their employees’ salaries leaked.
