DVPWA was inspired by famous dvwa project and bobby-tables xkcd comics. The purpose of this project is to implement real-world like application in Python with as many vulnerabilities as possible while having a good design and intentions.
This project was used as demonstration of vulnerabilities during my Web vulnerabilities presentation at EVO Summer Python Lab'17.
If for some reasons you cannot use docker to build the image, you can run the application on your host system.
- Python3.6.2
- PostgreSQL database for data storage
# Install application dependencies.
pip install -r requirements.txt
# Set up postgresql database Further I assume your db user
# is named postgres and database name is sqli
# Create database schema by applying migration 000
psql -U postgres --d sqli --host localhost --port 5432 \
-f migrations/000-init-schema.sql
# Load fixtures into database
psql -U postgres --d sqli --host localhost --port 5432 \
-f migrations/001-fixtures.sql
# Run application
python run.py
Then visit http://localhost:8000 in your favorite browser.
- Open http://localhost:8000.
- Open browser devtools.
- Get value for
AIOHTTP_SESSION
cookie. - Open http://localhost:8000 in the incognito tab.
- In the incognito tab, change cookie value to the one, obtained in step 3.
- In the normal tab (the one from steps 1-3) log in as any user.
- Refresh page in the incognito tab.
You are now logged in the incognito tab as user from step 6 as well.
Rotate session identifiers on every single login and logout. Rotate session identifiers on every user_id and/or permissions change.
- Open http://localhost:8000.
- Log in as
superadmin:superadmin
. - Go to http://localhost:8000/students/.
- Add new student with the name
Robert'); DROP TABLE students CASCADE; --
.
Table "students" is deleted from database. You observe error message, which says: _"relation "students" does not exist"_.
Never construct database queries using string concatenation. Use library-provided way to pass parameters and query separated. Use ORM.
Fill in review content with the following payload:
<b>Is this bold?</b> Yes!
Submit the review by clicking "Save" button.
Observe the newly created review. Note that text "Is it bold?" is bold, which means review content is probably neither sanitized on input nor escaped on output.
Fill in review content with the following payload:
<script> alert('I am a stored XSS. Your cookies are: ' + document.cookie); </script>
Submit the review by clicking "Save" button.
Observe the result.
Now whenever you load http://localhost:8000/courses/1, you will receive an
alert, which displays your cookie. You can play with different ways to inject
your custom javascript to the page now: event handlers (i.e. <img
src="nonexistent" onerror="alert(document.cookie)">
, links with javascript
targets, etc.
Escape all untrusted content, when you output it. In this example, to mitigate
this kind of attack you can set autoescape=True
when setting up templating
engine (Jinja2) in sqli/app.py
.
You can also sanitize text, when users input it and prohibit different kinds of
code injection.
As per check_paswword function and database initialization script, passwords are not stored in the database themselves, but their md5 hashes.
Here are the problems with such approach:
As hash function produces same output for same input, same passwords will produce the same hash. Passwords are vulnerable to statistical analysis: it is possible to determine how many people use the same password, how popular the password is, etc:
sqli=# select pwd_hash, array_agg(username), count(*) sqli-# from "users" sqli-# group by pwd_hash sqli-# order by count(*) desc; pwd_hash | array_agg | count ----------------------------------+----------------+------- 5f4dcc3b5aa765d61d8327deb882cf99 | {j.doe,s.king} | 2 1da0bac388e8e0409a83e121e1af6ef4 | {p.parker} | 1 17c4520f6cfd1ab53d8745e84681eb49 | {superadmin} | 1 (3 rows)
Md5 is considered quite a weak hash, thus collisions can be easily found. Moreover, this hash is easy to bruteforce, as well as a lot of rainbow tables exists for md5. For example, CrackStation website can be used for such purposes.
Password themselves should never be stored in database. Special hash functions for passwords exist, such as argon2, bcrypt, pbkdf2. These functions should be used instead of plain text passwords or weak hashes like md5, or fast hash functions like sha1, sha2. For examples, see password hashing section on PyNaCL documentation.
TBA