Skip to content

πŸ’₯ slaps roof of app // this bad boy can fit so many security exploits in it

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

hayesall/bottle-breaker

bottle-breaker 🍼πŸ’₯

A small flask application to demonstrate potential security vulnerabilities that may come up when writing web applications. i.e.: cross-site scripting (XSS) vulnerabilities and SQL injection attacks.

Screen recording of interacting with the site. First signing up as new user hayesall, then making a post saying Hi World!

Setup and run

1. Clone the repository

VSCode: New Window > Clone Git Repository

https://github.com/hayesall/bottle-breaker.git

Terminal

git clone https://github.com/hayesall/bottle-breaker.git
cd bottle-breaker

2. Create a virtual environment and install dependencies

Linux / Chromebook

python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

MacOS

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Windows + PowerShell (or see venv docs for your platform)

python -m venv venv
.\venv\Scripts\Activate.ps1

# Windows seems to take issue with the 'requirements.txt' file, try this:
pip install Flask==2.2.3 Flask-Login==0.6.2 Flask-WTF==1.1.1 WTForms==3.0.1

Scavenger Hunt

1. Make a post as a different user

Problem 1 Hints
Hint #1

The post form on the home page is vulnerable. View page source, read how the POST method works, and see if you can make a post as a different user.

Hint #2

The page inspector or developer tools (in Firefox, Chrome, Opera, etc.) can be accessed by clicking on an element and selecting "Inspect", or with the shortcut CTRL + SHIFT + I / ⌘ + OPTION + I.

If you modify something on the page and click ENTER, the document object model displayed by your browser is updated. This includes all form elements.

Hint #3

The form on the home page lists something like: action="/make-post@author=hayesall"

Solution

Change the name of the user in the developer tools to something like: action="/make-post@author=alice", and click enter. When you make a post, the post is inserted into the database as alice instead of your username.

This form is vulnerable because it inserts the name of the logged-in user into the form, but does not validate that the user making the POST request is the same as the user who clicks the button.


2. Trigger JavaScript to execute when users view a post

Problem 2 Hints
Hint #1

The form on the home page allows users to style their posts with basic HTML formatting. For example, users can post something like:

<strong>Hello</strong> <em>World!</em>

... in order to style their posts with bold or italic formatting.

Hint #2

The index.html template includes something that says: {{ post.content | safe }}.

Normally, we should only use the safe filter on content that we have verified is safe.

Solution

The way we display posts includes a cross-site scripting (XSS) vulnerability. Users can post something like:

<script>alert('samy is my hero');</script>

When users view this post, the JavaScript gets executed, triggering an "Alert" message in their browser.


3. Delete a post made by another user

You can do this without SQL injection.

Problem 3 Hints
Hint #1

The delete_post method in app.py is vulnerable. Go to your profile, view page source, and figure out how the form submission works.

Hint #2

This is similar to the solution for the first problem.

Hint #3

user_profile.html inserts "Delete" buttons with the following code:

{% if current_user.id == username %}
<form action="{{ url_for('delete_post', username=username, post_id=post.id) }}" method="POST">
    <button type="submit" class="btn btn-danger rounded-0">X</button>
</form>
{% endif %}
Solution

Similar to the first problem, the backend uses an API that does not verify who is making a request to an endpoint. Furthermore, it does not even check whether the post number belongs to the user 😬

The user_profile.html template conditionally inserts delete buttons only when the logged in user is the same as the user viewing the page, but there is nothing stopping anyone from triggering the API on form submission.

Change the form action to something like:

/delete-post/hayesall/1

... and click the red submit button.


4. Use an SQL injection attack to drop the posts table

Problem 4 Hints
Hint #1

In SQL, -- is a comment, meaning that everything after -- is ignored by the parser.

DROP TABLE posts; -- This is a comment, everything after the double-dash is ignored
Hint #2

The architecture of an SQL injection attack is to write a query that is (1) valid SQL and passes the server's input validation and the database's query parser, but (2) does something unexpected.

For example, if we have a query that normally inserts a name into a table:

INSERT INTO users (username) VALUES ('alice');

... consider what would happen if alice was replaced with alice'); DROP TABLE posts; -- . The resulting query would be:

INSERT INTO users (username) VALUES ('alice'); DROP TABLE posts; -- ');
Hint #3

This site uses SQLite, which is normally built around the sql.execute() method. This takes a string as input and executes it as a query.

This method has some built-in security. For example: if you try to execute a query that contains a semicolon (i.e., multiple statements), it will fail with something like:

sqlite3.Warning: You can only execute one statement at a time.

If you have a local copy of the code, try finding which line might be vulnerable by grepping for a line of code that contains a non-standard execute call:

git grep -n 'self.curr.execute'

git grep -n returns the file and line number for each match.

Solution

Hints 1/2/3 should lead you toward the /settings page and "Change Username" form. The form is vulnerable to SQL injection because the change_username methods uses a dangerous pattern:

script = f"PRAGMA foreign_keys = ON; UPDATE users SET username = '{new_username}' WHERE username = '{old_username}';"
self.curr.executescript(script)

This is vulnerable because the executescript method allows multiple statements to be executed at once, and the code uses literal string interpolation (f-strings) to build up a command. If we can insert a semicolon into the new_username field and use our knowledge of our old_username, we can write a query that drops the posts table:

alice2' WHERE username = 'alice'; DROP TABLE posts; --

Legal Points

Disclaimer

This project is for educational purposes only. Understanding how exploits work is important, but you should not use this information to attack websites without the owner's consent. I am not responsible for damage you cause, and this knowledge should be applied toward preventing attacks, not launching them.

I live in the United States, and I am not a lawyer. For others in the United States, computer "hacking" is broadly defined and possibly punishable under the terms specified in the Computer Fraud and Abuse Act and related policy. Your local jurisdiction may have different terms, depending on the state or country where you reside.

Related readings for people in the United States:

License

This project is available under the terms of the MIT License or Apache 2.0 License, at your choosing. See the LICENSE-MIT or LICENSE-APACHE files for more information.

About

πŸ’₯ slaps roof of app // this bad boy can fit so many security exploits in it

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Code of conduct

Security policy

Stars

Watchers

Forks