├── .gitignore ├── README.md ├── lab1-info-introspection ├── Dockerfile ├── README.md ├── app.py ├── populate-database.py ├── requirements.txt ├── static │ ├── css │ │ ├── bootstrap-table.css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── datepicker3.css │ │ └── styles.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── img │ │ ├── .DS_Store │ │ ├── avatar.png │ │ ├── badge.svg │ │ └── logo.svg │ └── js │ │ ├── bootstrap-datepicker.js │ │ ├── bootstrap-table.js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ ├── chart-data.js │ │ ├── chart.min.js │ │ ├── easypiechart-data.js │ │ ├── easypiechart.js │ │ ├── html5shiv.min.js │ │ ├── jquery-1.11.1.min.js │ │ ├── jquery.min.js │ │ ├── lumino.glyphs.js │ │ └── respond.min.js └── templates │ ├── .DS_Store │ └── index.html ├── lab2-dos-resource-exhaustion ├── DoS-payload.txt ├── Dockerfile ├── README.md ├── app.py ├── populate-database.py ├── requirements.txt ├── static │ ├── css │ │ ├── bootstrap-table.css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── datepicker3.css │ │ └── styles.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── img │ │ ├── .DS_Store │ │ ├── avatar.png │ │ ├── badge.svg │ │ └── logo.svg │ └── js │ │ ├── bootstrap-datepicker.js │ │ ├── bootstrap-table.js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ ├── chart-data.js │ │ ├── chart.min.js │ │ ├── easypiechart-data.js │ │ ├── easypiechart.js │ │ ├── html5shiv.min.js │ │ ├── jquery-1.11.1.min.js │ │ ├── jquery.min.js │ │ ├── lumino.glyphs.js │ │ └── respond.min.js └── templates │ ├── .DS_Store │ └── index.html ├── lab3-mutation ├── Dockerfile ├── README.md ├── app.py ├── populate-database.py ├── requirements.txt ├── static │ ├── css │ │ ├── bootstrap-table.css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── datepicker3.css │ │ └── styles.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── img │ │ ├── GraphQL - Resource Exahustion.htm │ │ ├── GraphQL - Resource Exahustion_files │ │ │ ├── avatar.png │ │ │ ├── bootstrap-datepicker.js │ │ │ ├── bootstrap.min.css │ │ │ ├── bootstrap.min.js │ │ │ ├── chart-data.js │ │ │ ├── chart.min.js │ │ │ ├── datepicker3.css │ │ │ ├── easypiechart-data.js │ │ │ ├── easypiechart.js │ │ │ ├── jquery-1.11.1.min.js │ │ │ ├── logo.svg │ │ │ ├── lumino.glyphs.js │ │ │ └── styles.css │ │ ├── avatar.png │ │ ├── badge.svg │ │ ├── bones.png │ │ └── logo.svg │ └── js │ │ ├── bootstrap-datepicker.js │ │ ├── bootstrap-table.js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ ├── chart-data.js │ │ ├── chart.min.js │ │ ├── easypiechart-data.js │ │ ├── easypiechart.js │ │ ├── html5shiv.min.js │ │ ├── jquery-1.11.1.min.js │ │ ├── jquery.min.js │ │ ├── lumino.glyphs.js │ │ └── respond.min.js └── templates │ └── index.html ├── lab4-IDOR ├── Dockerfile ├── README.md ├── app.py ├── populate-database.py ├── requirements.txt ├── static │ ├── css │ │ ├── bootstrap-table.css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── datepicker3.css │ │ └── styles.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── img │ │ ├── avatar.png │ │ ├── badge.svg │ │ └── logo.svg │ └── js │ │ ├── bootstrap-datepicker.js │ │ ├── bootstrap-table.js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ ├── chart-data.js │ │ ├── chart.min.js │ │ ├── easypiechart-data.js │ │ ├── easypiechart.js │ │ ├── html5shiv.min.js │ │ ├── jquery-1.11.1.min.js │ │ ├── jquery.min.js │ │ ├── lumino.glyphs.js │ │ └── respond.min.js └── templates │ ├── index.html │ ├── login.html │ └── settings.html └── lab5-injections ├── Dockerfile ├── README.md ├── app.py ├── populate-database.py ├── requirements.txt ├── static ├── css │ ├── bootstrap-table.css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── datepicker3.css │ └── styles.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── img │ ├── GraphQL - Resource Exahustion.htm │ ├── GraphQL - Resource Exahustion_files │ │ ├── avatar.png │ │ ├── bootstrap-datepicker.js │ │ ├── bootstrap.min.css │ │ ├── bootstrap.min.js │ │ ├── chart-data.js │ │ ├── chart.min.js │ │ ├── datepicker3.css │ │ ├── easypiechart-data.js │ │ ├── easypiechart.js │ │ ├── jquery-1.11.1.min.js │ │ ├── logo.svg │ │ ├── lumino.glyphs.js │ │ └── styles.css │ ├── avatar.png │ ├── badge.svg │ ├── bones.png │ └── logo.svg └── js │ ├── bootstrap-datepicker.js │ ├── bootstrap-table.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── chart-data.js │ ├── chart.min.js │ ├── easypiechart-data.js │ ├── easypiechart.js │ ├── html5shiv.min.js │ ├── jquery-1.11.1.min.js │ ├── jquery.min.js │ ├── lumino.glyphs.js │ └── respond.min.js └── templates ├── admin.html ├── index.html ├── login.html └── settings.html /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .DS_Store 3 | *.sqlite 4 | *.db -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL security 101 2 | 3 | 4 | 5 | GraphQL is quickly becoming the alternative to REST API, being able to request a specified set of data across multiple resources within a single request. But with great power come great security risks. A single point of failure could allow attackers to create complex queries and exhaust resources (DoS), or bypass authorization to retrieve unauthorized information. This hands-on workhop is a prefect match boost your GraphQL skills, and be able to exploit the wrong implementation of the framework. 6 | 7 | ### Topics include 8 | 9 | - Get familiar with GraphQL (mutation, queries,schema and types) 10 | - Introspection: information disclosure 11 | - /graphql as a single point of failure (DoS attacks) 12 | - IDOR, Broken Access control and Injection in GraphQL 13 | - How to avoid it 14 | 15 | The workshop is meant for developers, architects and security folks 16 | 17 | ## Interested in organizing a private workshop? 18 | 19 | Check our full program here: https://1337.dcodx.com/trainings/workshops 20 | 21 | Contact us at: 22 | 23 | -------------------------------------------------------------------------------- /lab1-info-introspection/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.7 2 | MAINTAINER Davide Cioccia 3 | RUN apk update --no-cache && apk add python3 \ 4 | python3-dev \ 5 | py3-pip \ 6 | bash \ 7 | build-base \ 8 | gcc \ 9 | musl-dev \ 10 | g++ 11 | 12 | COPY . /app 13 | WORKDIR /app 14 | RUN pip3 install --upgrade pip 15 | RUN pip3 install -r requirements.txt 16 | RUN python3 populate-database.py 17 | CMD [ "python3", "app.py"] 18 | -------------------------------------------------------------------------------- /lab1-info-introspection/README.md: -------------------------------------------------------------------------------- 1 | # GraphQL Introspection 2 | 3 | The application uses GraphQL to retrieve Users and Posts for the defdev.eu new blog. 4 | 5 | > Run the application 6 | 7 | ```sh 8 | docker build . -t graphql/intro && docker run -ti -p 5000:5000 graphql/intro 9 | ``` 10 | 11 | Great now the app is running. Browse to `http://0.0.0.0:5000/` 12 | 13 | ## Discovery 14 | 15 | As soon as we browse on `http://0.0.0.0:5000` we see the few posts published by 2 users 16 | 17 | 18 | 19 | 20 | ## Exploitation 21 | 22 | We want to use the introspection feature (enabled) in this case, to understand more about what queries are supported. 23 | 24 | 25 | Let' use the `GraphiQL` UI to send queries to the backend and discover what is available. 26 | 27 | Go to `http://0.0.0.0:5000/graphql`. We can query the generic `__schema` using: 28 | 29 | ``` 30 | { 31 | __schema { 32 | types { 33 | name 34 | } 35 | } 36 | } 37 | ``` 38 | 39 | The application gives us interesting `Types`. Let's explore the `UserObject` one. If we build a more complex query we can ask for more information, exploring every field available. Let's send the following: 40 | 41 | ``` 42 | { 43 | __type(name: "UserObject") { 44 | name 45 | fields { 46 | name 47 | type { 48 | name 49 | kind 50 | ofType { 51 | name 52 | kind 53 | } 54 | } 55 | } 56 | } 57 | } 58 | ``` 59 | In this case, for each field we we want to know what are the subfields and of which type. 60 | 61 | The application will answer with: 62 | 63 | ``` 64 | { 65 | "data": { 66 | "__type": { 67 | "name": "UserObject", 68 | "fields": [ 69 | { 70 | "name": "uuid", 71 | "type": { 72 | "name": null, 73 | "kind": "NON_NULL", 74 | "ofType": { 75 | "name": "ID", 76 | "kind": "SCALAR" 77 | } 78 | } 79 | }, 80 | { 81 | "name": "username", 82 | "type": { 83 | "name": "String", 84 | "kind": "SCALAR", 85 | "ofType": null 86 | } 87 | }, 88 | { 89 | "name": "isAdmin", 90 | "type": { 91 | "name": "Boolean", 92 | "kind": "SCALAR", 93 | "ofType": null 94 | } 95 | }, 96 | { 97 | "name": "posts", 98 | "type": { 99 | "name": "PostObjectConnection", 100 | "kind": "OBJECT", 101 | "ofType": null 102 | } 103 | }, 104 | { 105 | "name": "id", 106 | "type": { 107 | "name": null, 108 | "kind": "NON_NULL", 109 | "ofType": { 110 | "name": "ID", 111 | "kind": "SCALAR" 112 | } 113 | } 114 | } 115 | ] 116 | } 117 | } 118 | } 119 | ``` 120 | > BINGO! We have some good information here 121 | 122 | We can see that there is an interesting field `isAdmin`, that we can use to find out who is the admin of the application. 123 | 124 | 125 | Now we just need to query all the Users. To do that, let's see if there is a query available. We can use the following syntax: 126 | 127 | ``` 128 | query availableQueries { 129 | __schema { 130 | queryType { 131 | fields { 132 | name 133 | description 134 | } 135 | } 136 | } 137 | } 138 | ``` 139 | 140 | That will give us the `allUsers` query. Now we need to understand what are the fields. We can do that in different ways, using GraphiQL or doing some more introspection. In this case we use GraphiQL, sending the following query: 141 | 142 | ``` 143 | { 144 | allUsers { 145 | edges { 146 | node { 147 | username 148 | isAdmin 149 | } 150 | } 151 | } 152 | } 153 | 154 | ``` 155 | 156 | ## Fix 157 | 158 | Implement authorization on graphql endpoint. Although authenticated users could query the information, you should not map sensitive information into the type defined into the schema. 159 | 160 | 161 | -------------------------------------------------------------------------------- /lab1-info-introspection/app.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | from flask import Flask, render_template 3 | from flask_sqlalchemy import SQLAlchemy 4 | import os 5 | import graphene 6 | from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField 7 | from flask_graphql import GraphQLView 8 | 9 | 10 | 11 | 12 | basedir = os.path.abspath(os.path.dirname(__file__)) 13 | 14 | 15 | # app initialization 16 | app = Flask(__name__, static_url_path='/static', static_folder='static') 17 | app.debug = True 18 | 19 | # Configs 20 | 21 | ## Configuring the database 22 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite') 23 | app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True 24 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True 25 | 26 | # Modules 27 | 28 | ## Declaring the database 29 | db = SQLAlchemy(app) 30 | 31 | # Models 32 | 33 | 34 | # 1 User -> N Post 35 | # 36 | # 1 Post -> 1 User 37 | # 38 | 39 | 40 | 41 | class User(db.Model): 42 | __tablename__ = 'users' 43 | uuid = db.Column(db.Integer, primary_key=True) 44 | username = db.Column(db.String(256), index=True, unique=True) 45 | isAdmin = db.Column(db.Boolean) 46 | #posts = db.relationship('Post', backref='users') ## HERE is the problem, that enables to create recursive queries 47 | posts = db.relationship('Post', backref='author') 48 | 49 | def __repr__(self): 50 | return '' % self.username 51 | class Post(db.Model): 52 | __tablename__ = 'posts' 53 | uuid = db.Column(db.Integer, primary_key=True) 54 | title = db.Column(db.String(256), index=True) 55 | body = db.Column(db.Text) 56 | author_id = db.Column(db.Integer, db.ForeignKey('users.uuid')) 57 | 58 | 59 | def __repr__(self): 60 | return '' % self.title 61 | 62 | # Schema Objects 63 | 64 | class PostObject(SQLAlchemyObjectType): 65 | class Meta: 66 | model = Post 67 | interfaces = (graphene.relay.Node, ) 68 | class UserObject(SQLAlchemyObjectType): 69 | class Meta: 70 | model = User 71 | interfaces = (graphene.relay.Node, ) 72 | class Query(graphene.ObjectType): 73 | node = graphene.relay.Node.Field() 74 | all_posts = SQLAlchemyConnectionField(PostObject) 75 | all_users = SQLAlchemyConnectionField(UserObject) 76 | schema = graphene.Schema(query=Query) 77 | 78 | 79 | # Routes 80 | 81 | app.add_url_rule( 82 | '/graphql', 83 | view_func=GraphQLView.as_view( 84 | 'graphql', 85 | schema=schema, 86 | graphiql=True # for having the GraphiQL interface 87 | ) 88 | ) 89 | 90 | @app.route('/') 91 | def index(): 92 | 93 | #Implement logic to retrieve posts 94 | 95 | return render_template("index.html",username = "jhon", body="KADJ SDKLASJDKASJ SKDJ S",title="Wow I did not see this" ) 96 | 97 | 98 | if __name__ == '__main__': 99 | app.run('0.0.0.0') 100 | -------------------------------------------------------------------------------- /lab1-info-introspection/populate-database.py: -------------------------------------------------------------------------------- 1 | from app import db, User, Post 2 | db.create_all() 3 | john = User(username='johndoe') 4 | john.isAdmin = False 5 | post = Post() 6 | post.title = "Hello World" 7 | post.body = "This is the first post of jhon" 8 | post.author = john 9 | db.session.add(post) 10 | db.session.add(john) 11 | jim = User(username='jimcarry') 12 | jim.isAdmin = True 13 | post2 = Post() 14 | post2.title = "Woooow" 15 | post2.body = "I'm the maaaaask" 16 | post2.author = jim 17 | db.session.add(post2) 18 | db.session.add(jim) 19 | post3 = Post() 20 | post3.title = "Second Post Jhon" 21 | post3.body = "This is the second post of jhon" 22 | post3.author = john 23 | db.session.commit() 24 | -------------------------------------------------------------------------------- /lab1-info-introspection/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask-graphql 3 | flask-migrate 4 | flask-sqlalchemy 5 | graphql-core==2.2.1 6 | graphene==2.1.8 7 | graphene-sqlalchemy -------------------------------------------------------------------------------- /lab1-info-introspection/static/css/bootstrap-table.css: -------------------------------------------------------------------------------- 1 | .table { 2 | margin-bottom: 0 !important; 3 | border-bottom: 1px solid #dddddd; 4 | border-collapse: collapse !important; 5 | border-radius: 1px; 6 | } 7 | 8 | .fixed-table-container { 9 | position: relative; 10 | clear: both; 11 | border: 1px solid #dddddd; 12 | border-radius: 4px; 13 | -webkit-border-radius: 4px; 14 | -moz-border-radius: 4px; 15 | } 16 | 17 | .fixed-table-header { 18 | overflow: hidden; 19 | border-radius: 4px 4px 0 0; 20 | -webkit-border-radius: 4px 4px 0 0; 21 | -moz-border-radius: 4px 4px 0 0; 22 | } 23 | 24 | .fixed-table-body { 25 | overflow-x: auto; 26 | overflow-y: auto; 27 | height: 100%; 28 | } 29 | 30 | .fixed-table-container table { 31 | width: 100%; 32 | } 33 | 34 | .fixed-table-container thead th { 35 | height: 0; 36 | padding: 0; 37 | margin: 0; 38 | border-left: 1px solid #dddddd; 39 | } 40 | 41 | .fixed-table-container thead th:first-child { 42 | border-left: none; 43 | } 44 | 45 | .fixed-table-container thead th .th-inner { 46 | padding: 8px; 47 | line-height: 24px; 48 | vertical-align: top; 49 | overflow: hidden; 50 | text-overflow: ellipsis; 51 | white-space: nowrap; 52 | } 53 | 54 | .fixed-table-container thead th .sortable { 55 | cursor: pointer; 56 | } 57 | 58 | .fixed-table-container tbody td { 59 | border-left: 1px solid #dddddd; 60 | } 61 | 62 | .fixed-table-container tbody tr:first-child td { 63 | border-top: none; 64 | } 65 | 66 | .fixed-table-container tbody td:first-child { 67 | border-left: none; 68 | } 69 | 70 | /* the same color with .active */ 71 | .fixed-table-container tbody .selected td { 72 | background-color: #f5f5f5; 73 | } 74 | 75 | .fixed-table-container .bs-checkbox { 76 | text-align: center; 77 | } 78 | 79 | .fixed-table-container .bs-checkbox .th-inner { 80 | padding: 8px 0; 81 | } 82 | 83 | .fixed-table-container input[type="radio"], 84 | .fixed-table-container input[type="checkbox"] { 85 | margin: 0 auto !important; 86 | } 87 | 88 | .fixed-table-container .no-records-found { 89 | text-align: center; 90 | } 91 | 92 | 93 | .fixed-table-pagination .pagination, 94 | .fixed-table-pagination .pagination-detail { 95 | margin-top: 10px; 96 | margin-bottom: 10px; 97 | } 98 | 99 | .fixed-table-pagination .pagination a { 100 | padding: 6px 12px; 101 | line-height: 1.428571429; 102 | } 103 | 104 | .fixed-table-pagination .pagination-info { 105 | line-height: 34px; 106 | margin-right: 5px; 107 | } 108 | 109 | .fixed-table-pagination .btn-group { 110 | position: relative; 111 | display: inline-block; 112 | vertical-align: middle; 113 | } 114 | 115 | .fixed-table-pagination .dropup .dropdown-menu { 116 | margin-bottom: 0; 117 | } 118 | 119 | .fixed-table-pagination .page-list { 120 | display: inline-block; 121 | } 122 | 123 | .fixed-table-toolbar .columns { 124 | margin-left: 5px; 125 | } 126 | 127 | .fixed-table-toolbar .columns label { 128 | display: block; 129 | padding: 3px 20px; 130 | clear: both; 131 | font-weight: normal; 132 | line-height: 1.428571429; 133 | } 134 | 135 | .fixed-table-toolbar .bars, 136 | .fixed-table-toolbar .search, 137 | .fixed-table-toolbar .columns { 138 | position: relative; 139 | margin-top: 10px; 140 | margin-bottom: 10px; 141 | line-height: 34px; 142 | } 143 | 144 | .fixed-table-pagination li.disabled a { 145 | pointer-events: none; 146 | cursor: default; 147 | } 148 | 149 | .fixed-table-loading { 150 | display: none; 151 | position: absolute; 152 | top: 42px; 153 | right: 0; 154 | bottom: 0; 155 | left: 0; 156 | z-index: 99; 157 | background-color: #fff; 158 | text-align: center; 159 | } 160 | 161 | .fixed-table-body .card-view .title { 162 | font-weight: bold; 163 | display: inline-block; 164 | min-width: 30%; 165 | text-align: left !important; 166 | } 167 | 168 | /* support bootstrap 2 */ 169 | .fixed-table-body thead th .th-inner { 170 | box-sizing: border-box; 171 | } 172 | 173 | .table th, .table td { 174 | vertical-align: middle; 175 | box-sizing: border-box; 176 | } 177 | 178 | .fixed-table-toolbar .dropdown-menu { 179 | text-align: left; 180 | max-height: 300px; 181 | overflow: auto; 182 | } 183 | 184 | .fixed-table-toolbar .btn-group>.btn-group { 185 | display: inline-block; 186 | margin-left: -1px !important; 187 | } 188 | 189 | .fixed-table-toolbar .btn-group>.btn-group>.btn { 190 | border-radius: 0; 191 | } 192 | 193 | .fixed-table-toolbar .btn-group>.btn-group:first-child>.btn { 194 | border-top-left-radius: 4px; 195 | border-bottom-left-radius: 4px; 196 | } 197 | 198 | .fixed-table-toolbar .btn-group>.btn-group:last-child>.btn { 199 | border-top-right-radius: 4px; 200 | border-bottom-right-radius: 4px; 201 | } 202 | 203 | .table>thead>tr>th { 204 | vertical-align: bottom; 205 | border-bottom: 2px solid #ddd; 206 | } 207 | 208 | /* support bootstrap 3 */ 209 | .table thead>tr>th { 210 | padding: 0; 211 | margin: 0; 212 | } 213 | 214 | .pull-right .dropdown-menu { 215 | right: 0; 216 | left: auto; 217 | } 218 | 219 | /* calculate scrollbar width */ 220 | p.fixed-table-scroll-inner { 221 | width: 100%; 222 | height: 200px; 223 | } 224 | 225 | div.fixed-table-scroll-outer { 226 | top: 0; 227 | left: 0; 228 | visibility: hidden; 229 | width: 200px; 230 | height: 150px; 231 | overflow: hidden; 232 | } -------------------------------------------------------------------------------- /lab1-info-introspection/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab1-info-introspection/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /lab1-info-introspection/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab1-info-introspection/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /lab1-info-introspection/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab1-info-introspection/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /lab1-info-introspection/static/img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab1-info-introspection/static/img/.DS_Store -------------------------------------------------------------------------------- /lab1-info-introspection/static/img/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab1-info-introspection/static/img/avatar.png -------------------------------------------------------------------------------- /lab1-info-introspection/static/img/badge.svg: -------------------------------------------------------------------------------- 1 | chatchaton gitteron gitter -------------------------------------------------------------------------------- /lab1-info-introspection/static/js/chart-data.js: -------------------------------------------------------------------------------- 1 | var randomScalingFactor = function(){ return Math.round(Math.random()*1000)}; 2 | 3 | var lineChartData = { 4 | labels : ["January","February","March","April","May","June","July"], 5 | datasets : [ 6 | { 7 | label: "My First dataset", 8 | fillColor : "rgba(220,220,220,0.2)", 9 | strokeColor : "rgba(220,220,220,1)", 10 | pointColor : "rgba(220,220,220,1)", 11 | pointStrokeColor : "#fff", 12 | pointHighlightFill : "#fff", 13 | pointHighlightStroke : "rgba(220,220,220,1)", 14 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 15 | }, 16 | { 17 | label: "My Second dataset", 18 | fillColor : "rgba(48, 164, 255, 0.2)", 19 | strokeColor : "rgba(48, 164, 255, 1)", 20 | pointColor : "rgba(48, 164, 255, 1)", 21 | pointStrokeColor : "#fff", 22 | pointHighlightFill : "#fff", 23 | pointHighlightStroke : "rgba(48, 164, 255, 1)", 24 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 25 | } 26 | ] 27 | 28 | } 29 | 30 | var barChartData = { 31 | labels : ["January","February","March","April","May","June","July"], 32 | datasets : [ 33 | { 34 | fillColor : "rgba(220,220,220,0.5)", 35 | strokeColor : "rgba(220,220,220,0.8)", 36 | highlightFill: "rgba(220,220,220,0.75)", 37 | highlightStroke: "rgba(220,220,220,1)", 38 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 39 | }, 40 | { 41 | fillColor : "rgba(48, 164, 255, 0.2)", 42 | strokeColor : "rgba(48, 164, 255, 0.8)", 43 | highlightFill : "rgba(48, 164, 255, 0.75)", 44 | highlightStroke : "rgba(48, 164, 255, 1)", 45 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 46 | } 47 | ] 48 | 49 | } 50 | 51 | var pieData = [ 52 | { 53 | value: 300, 54 | color:"#30a5ff", 55 | highlight: "#62b9fb", 56 | label: "Blue" 57 | }, 58 | { 59 | value: 50, 60 | color: "#ffb53e", 61 | highlight: "#fac878", 62 | label: "Orange" 63 | }, 64 | { 65 | value: 100, 66 | color: "#1ebfae", 67 | highlight: "#3cdfce", 68 | label: "Teal" 69 | }, 70 | { 71 | value: 120, 72 | color: "#f9243f", 73 | highlight: "#f6495f", 74 | label: "Red" 75 | } 76 | 77 | ]; 78 | 79 | var doughnutData = [ 80 | { 81 | value: 300, 82 | color:"#30a5ff", 83 | highlight: "#62b9fb", 84 | label: "Blue" 85 | }, 86 | { 87 | value: 50, 88 | color: "#ffb53e", 89 | highlight: "#fac878", 90 | label: "Orange" 91 | }, 92 | { 93 | value: 100, 94 | color: "#1ebfae", 95 | highlight: "#3cdfce", 96 | label: "Teal" 97 | }, 98 | { 99 | value: 120, 100 | color: "#f9243f", 101 | highlight: "#f6495f", 102 | label: "Red" 103 | } 104 | 105 | ]; 106 | 107 | window.onload = function(){ 108 | var chart1 = document.getElementById("line-chart").getContext("2d"); 109 | window.myLine = new Chart(chart1).Line(lineChartData, { 110 | responsive: true 111 | }); 112 | var chart2 = document.getElementById("bar-chart").getContext("2d"); 113 | window.myBar = new Chart(chart2).Bar(barChartData, { 114 | responsive : true 115 | }); 116 | var chart3 = document.getElementById("doughnut-chart").getContext("2d"); 117 | window.myDoughnut = new Chart(chart3).Doughnut(doughnutData, {responsive : true 118 | }); 119 | var chart4 = document.getElementById("pie-chart").getContext("2d"); 120 | window.myPie = new Chart(chart4).Pie(pieData, {responsive : true 121 | }); 122 | 123 | }; -------------------------------------------------------------------------------- /lab1-info-introspection/static/js/easypiechart-data.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('#easypiechart-teal').easyPieChart({ 3 | scaleColor: false, 4 | barColor: '#1ebfae' 5 | }); 6 | }); 7 | 8 | $(function() { 9 | $('#easypiechart-orange').easyPieChart({ 10 | scaleColor: false, 11 | barColor: '#ffb53e' 12 | }); 13 | }); 14 | 15 | $(function() { 16 | $('#easypiechart-red').easyPieChart({ 17 | scaleColor: false, 18 | barColor: '#f9243f' 19 | }); 20 | }); 21 | 22 | $(function() { 23 | $('#easypiechart-blue').easyPieChart({ 24 | scaleColor: false, 25 | barColor: '#30a5ff' 26 | }); 27 | }); 28 | 29 | $('#calendar').datepicker({ 30 | }); 31 | -------------------------------------------------------------------------------- /lab1-info-introspection/static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document); -------------------------------------------------------------------------------- /lab1-info-introspection/static/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill 2 | * Copyright 2014 Scott Jehl 3 | * Licensed under MIT 4 | * http://j.mp/respondjs */ 5 | 6 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b 2 | 3 | 4 | 5 | 6 | 7 | GraphQL - Resource Exahustion 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 38 | 39 | 47 | 48 |
49 |
50 | 51 |
52 | 53 |
54 |
55 |

56 |
57 |
58 | 59 | 60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 89 | 90 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.7 2 | MAINTAINER Davide Cioccia 3 | RUN apk update --no-cache && apk add python3 \ 4 | python3-dev \ 5 | py3-pip \ 6 | bash \ 7 | gcc \ 8 | musl-dev \ 9 | g++ 10 | 11 | COPY . /app 12 | WORKDIR /app 13 | RUN pip3 install -r requirements.txt 14 | RUN python3 populate-database.py 15 | CMD [ "python3", "app.py"] -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/app.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | from flask import Flask, render_template 3 | from flask_sqlalchemy import SQLAlchemy 4 | import os 5 | import graphene 6 | from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField 7 | from flask_graphql import GraphQLView 8 | 9 | 10 | 11 | 12 | basedir = os.path.abspath(os.path.dirname(__file__)) 13 | 14 | 15 | # app initialization 16 | app = Flask(__name__, static_url_path='/static', static_folder='static') 17 | app.debug = True 18 | 19 | # Configs 20 | 21 | ## Configuring the database 22 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite') 23 | app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True 24 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True 25 | 26 | # Modules 27 | 28 | ## Declaring the database 29 | db = SQLAlchemy(app) 30 | 31 | 32 | # Models 33 | 34 | 35 | # 1 User -> N Post 36 | # 37 | # 1 Post -> 1 User 38 | # 39 | 40 | 41 | class User(db.Model): 42 | __tablename__ = 'users' 43 | uuid = db.Column(db.Integer, primary_key=True) 44 | username = db.Column(db.String(256), index=True, unique=True) 45 | posts = db.relationship('Post', backref='users') ## HERE is the problem, that enables to create recursive queries 46 | 47 | def __repr__(self): 48 | return '' % self.username 49 | class Post(db.Model): 50 | __tablename__ = 'posts' 51 | uuid = db.Column(db.Integer, primary_key=True) 52 | title = db.Column(db.String(256), index=True) 53 | body = db.Column(db.Text) 54 | author_id = db.Column(db.Integer, db.ForeignKey('users.uuid')) 55 | author = db.relationship('User', backref='post') 56 | 57 | def __repr__(self): 58 | return '' % self.title 59 | 60 | # Schema Objects 61 | 62 | class PostObject(SQLAlchemyObjectType): 63 | class Meta: 64 | model = Post 65 | interfaces = (graphene.relay.Node, ) 66 | class UserObject(SQLAlchemyObjectType): 67 | class Meta: 68 | model = User 69 | interfaces = (graphene.relay.Node, ) 70 | class Query(graphene.ObjectType): 71 | node = graphene.relay.Node.Field() 72 | all_posts = SQLAlchemyConnectionField(PostObject) 73 | all_users = SQLAlchemyConnectionField(UserObject) 74 | schema = graphene.Schema(query=Query) 75 | 76 | 77 | # Routes 78 | 79 | app.add_url_rule( 80 | '/graphql', 81 | view_func=GraphQLView.as_view( 82 | 'graphql', 83 | schema=schema, 84 | graphiql=True # for having the GraphiQL interface 85 | ) 86 | ) 87 | 88 | @app.route('/') 89 | def index(): 90 | 91 | #Implement logic to retrieve posts 92 | 93 | return render_template("index.html" ) 94 | 95 | 96 | if __name__ == '__main__': 97 | app.run('0.0.0.0') 98 | -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/populate-database.py: -------------------------------------------------------------------------------- 1 | from app import db, User, Post 2 | db.create_all() 3 | john = User(username='johndoe') 4 | john.isAdmin = False 5 | post = Post() 6 | post.title = "Hello World" 7 | post.body = "This is the first post of jhon" 8 | post.author = john 9 | db.session.add(post) 10 | db.session.add(john) 11 | jim = User(username='jimcarry') 12 | jim.isAdmin = True 13 | post2 = Post() 14 | post2.title = "Woooow" 15 | post2.body = "I'm the maaaaask" 16 | post2.author = jim 17 | db.session.add(post2) 18 | db.session.add(jim) 19 | post3 = Post() 20 | post3.title = "Second Post Jhon" 21 | post3.body = "This is the second post of jhon" 22 | post3.author = john 23 | db.session.commit() 24 | -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask-graphql 3 | flask-migrate 4 | flask-sqlalchemy 5 | graphql-core==2.2.1 6 | graphene==2.1.8 7 | graphene-sqlalchemy -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/css/bootstrap-table.css: -------------------------------------------------------------------------------- 1 | .table { 2 | margin-bottom: 0 !important; 3 | border-bottom: 1px solid #dddddd; 4 | border-collapse: collapse !important; 5 | border-radius: 1px; 6 | } 7 | 8 | .fixed-table-container { 9 | position: relative; 10 | clear: both; 11 | border: 1px solid #dddddd; 12 | border-radius: 4px; 13 | -webkit-border-radius: 4px; 14 | -moz-border-radius: 4px; 15 | } 16 | 17 | .fixed-table-header { 18 | overflow: hidden; 19 | border-radius: 4px 4px 0 0; 20 | -webkit-border-radius: 4px 4px 0 0; 21 | -moz-border-radius: 4px 4px 0 0; 22 | } 23 | 24 | .fixed-table-body { 25 | overflow-x: auto; 26 | overflow-y: auto; 27 | height: 100%; 28 | } 29 | 30 | .fixed-table-container table { 31 | width: 100%; 32 | } 33 | 34 | .fixed-table-container thead th { 35 | height: 0; 36 | padding: 0; 37 | margin: 0; 38 | border-left: 1px solid #dddddd; 39 | } 40 | 41 | .fixed-table-container thead th:first-child { 42 | border-left: none; 43 | } 44 | 45 | .fixed-table-container thead th .th-inner { 46 | padding: 8px; 47 | line-height: 24px; 48 | vertical-align: top; 49 | overflow: hidden; 50 | text-overflow: ellipsis; 51 | white-space: nowrap; 52 | } 53 | 54 | .fixed-table-container thead th .sortable { 55 | cursor: pointer; 56 | } 57 | 58 | .fixed-table-container tbody td { 59 | border-left: 1px solid #dddddd; 60 | } 61 | 62 | .fixed-table-container tbody tr:first-child td { 63 | border-top: none; 64 | } 65 | 66 | .fixed-table-container tbody td:first-child { 67 | border-left: none; 68 | } 69 | 70 | /* the same color with .active */ 71 | .fixed-table-container tbody .selected td { 72 | background-color: #f5f5f5; 73 | } 74 | 75 | .fixed-table-container .bs-checkbox { 76 | text-align: center; 77 | } 78 | 79 | .fixed-table-container .bs-checkbox .th-inner { 80 | padding: 8px 0; 81 | } 82 | 83 | .fixed-table-container input[type="radio"], 84 | .fixed-table-container input[type="checkbox"] { 85 | margin: 0 auto !important; 86 | } 87 | 88 | .fixed-table-container .no-records-found { 89 | text-align: center; 90 | } 91 | 92 | 93 | .fixed-table-pagination .pagination, 94 | .fixed-table-pagination .pagination-detail { 95 | margin-top: 10px; 96 | margin-bottom: 10px; 97 | } 98 | 99 | .fixed-table-pagination .pagination a { 100 | padding: 6px 12px; 101 | line-height: 1.428571429; 102 | } 103 | 104 | .fixed-table-pagination .pagination-info { 105 | line-height: 34px; 106 | margin-right: 5px; 107 | } 108 | 109 | .fixed-table-pagination .btn-group { 110 | position: relative; 111 | display: inline-block; 112 | vertical-align: middle; 113 | } 114 | 115 | .fixed-table-pagination .dropup .dropdown-menu { 116 | margin-bottom: 0; 117 | } 118 | 119 | .fixed-table-pagination .page-list { 120 | display: inline-block; 121 | } 122 | 123 | .fixed-table-toolbar .columns { 124 | margin-left: 5px; 125 | } 126 | 127 | .fixed-table-toolbar .columns label { 128 | display: block; 129 | padding: 3px 20px; 130 | clear: both; 131 | font-weight: normal; 132 | line-height: 1.428571429; 133 | } 134 | 135 | .fixed-table-toolbar .bars, 136 | .fixed-table-toolbar .search, 137 | .fixed-table-toolbar .columns { 138 | position: relative; 139 | margin-top: 10px; 140 | margin-bottom: 10px; 141 | line-height: 34px; 142 | } 143 | 144 | .fixed-table-pagination li.disabled a { 145 | pointer-events: none; 146 | cursor: default; 147 | } 148 | 149 | .fixed-table-loading { 150 | display: none; 151 | position: absolute; 152 | top: 42px; 153 | right: 0; 154 | bottom: 0; 155 | left: 0; 156 | z-index: 99; 157 | background-color: #fff; 158 | text-align: center; 159 | } 160 | 161 | .fixed-table-body .card-view .title { 162 | font-weight: bold; 163 | display: inline-block; 164 | min-width: 30%; 165 | text-align: left !important; 166 | } 167 | 168 | /* support bootstrap 2 */ 169 | .fixed-table-body thead th .th-inner { 170 | box-sizing: border-box; 171 | } 172 | 173 | .table th, .table td { 174 | vertical-align: middle; 175 | box-sizing: border-box; 176 | } 177 | 178 | .fixed-table-toolbar .dropdown-menu { 179 | text-align: left; 180 | max-height: 300px; 181 | overflow: auto; 182 | } 183 | 184 | .fixed-table-toolbar .btn-group>.btn-group { 185 | display: inline-block; 186 | margin-left: -1px !important; 187 | } 188 | 189 | .fixed-table-toolbar .btn-group>.btn-group>.btn { 190 | border-radius: 0; 191 | } 192 | 193 | .fixed-table-toolbar .btn-group>.btn-group:first-child>.btn { 194 | border-top-left-radius: 4px; 195 | border-bottom-left-radius: 4px; 196 | } 197 | 198 | .fixed-table-toolbar .btn-group>.btn-group:last-child>.btn { 199 | border-top-right-radius: 4px; 200 | border-bottom-right-radius: 4px; 201 | } 202 | 203 | .table>thead>tr>th { 204 | vertical-align: bottom; 205 | border-bottom: 2px solid #ddd; 206 | } 207 | 208 | /* support bootstrap 3 */ 209 | .table thead>tr>th { 210 | padding: 0; 211 | margin: 0; 212 | } 213 | 214 | .pull-right .dropdown-menu { 215 | right: 0; 216 | left: auto; 217 | } 218 | 219 | /* calculate scrollbar width */ 220 | p.fixed-table-scroll-inner { 221 | width: 100%; 222 | height: 200px; 223 | } 224 | 225 | div.fixed-table-scroll-outer { 226 | top: 0; 227 | left: 0; 228 | visibility: hidden; 229 | width: 200px; 230 | height: 150px; 231 | overflow: hidden; 232 | } -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab2-dos-resource-exhaustion/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab2-dos-resource-exhaustion/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab2-dos-resource-exhaustion/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab2-dos-resource-exhaustion/static/img/.DS_Store -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/img/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab2-dos-resource-exhaustion/static/img/avatar.png -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/img/badge.svg: -------------------------------------------------------------------------------- 1 | chatchaton gitteron gitter -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/js/chart-data.js: -------------------------------------------------------------------------------- 1 | var randomScalingFactor = function(){ return Math.round(Math.random()*1000)}; 2 | 3 | var lineChartData = { 4 | labels : ["January","February","March","April","May","June","July"], 5 | datasets : [ 6 | { 7 | label: "My First dataset", 8 | fillColor : "rgba(220,220,220,0.2)", 9 | strokeColor : "rgba(220,220,220,1)", 10 | pointColor : "rgba(220,220,220,1)", 11 | pointStrokeColor : "#fff", 12 | pointHighlightFill : "#fff", 13 | pointHighlightStroke : "rgba(220,220,220,1)", 14 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 15 | }, 16 | { 17 | label: "My Second dataset", 18 | fillColor : "rgba(48, 164, 255, 0.2)", 19 | strokeColor : "rgba(48, 164, 255, 1)", 20 | pointColor : "rgba(48, 164, 255, 1)", 21 | pointStrokeColor : "#fff", 22 | pointHighlightFill : "#fff", 23 | pointHighlightStroke : "rgba(48, 164, 255, 1)", 24 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 25 | } 26 | ] 27 | 28 | } 29 | 30 | var barChartData = { 31 | labels : ["January","February","March","April","May","June","July"], 32 | datasets : [ 33 | { 34 | fillColor : "rgba(220,220,220,0.5)", 35 | strokeColor : "rgba(220,220,220,0.8)", 36 | highlightFill: "rgba(220,220,220,0.75)", 37 | highlightStroke: "rgba(220,220,220,1)", 38 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 39 | }, 40 | { 41 | fillColor : "rgba(48, 164, 255, 0.2)", 42 | strokeColor : "rgba(48, 164, 255, 0.8)", 43 | highlightFill : "rgba(48, 164, 255, 0.75)", 44 | highlightStroke : "rgba(48, 164, 255, 1)", 45 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 46 | } 47 | ] 48 | 49 | } 50 | 51 | var pieData = [ 52 | { 53 | value: 300, 54 | color:"#30a5ff", 55 | highlight: "#62b9fb", 56 | label: "Blue" 57 | }, 58 | { 59 | value: 50, 60 | color: "#ffb53e", 61 | highlight: "#fac878", 62 | label: "Orange" 63 | }, 64 | { 65 | value: 100, 66 | color: "#1ebfae", 67 | highlight: "#3cdfce", 68 | label: "Teal" 69 | }, 70 | { 71 | value: 120, 72 | color: "#f9243f", 73 | highlight: "#f6495f", 74 | label: "Red" 75 | } 76 | 77 | ]; 78 | 79 | var doughnutData = [ 80 | { 81 | value: 300, 82 | color:"#30a5ff", 83 | highlight: "#62b9fb", 84 | label: "Blue" 85 | }, 86 | { 87 | value: 50, 88 | color: "#ffb53e", 89 | highlight: "#fac878", 90 | label: "Orange" 91 | }, 92 | { 93 | value: 100, 94 | color: "#1ebfae", 95 | highlight: "#3cdfce", 96 | label: "Teal" 97 | }, 98 | { 99 | value: 120, 100 | color: "#f9243f", 101 | highlight: "#f6495f", 102 | label: "Red" 103 | } 104 | 105 | ]; 106 | 107 | window.onload = function(){ 108 | var chart1 = document.getElementById("line-chart").getContext("2d"); 109 | window.myLine = new Chart(chart1).Line(lineChartData, { 110 | responsive: true 111 | }); 112 | var chart2 = document.getElementById("bar-chart").getContext("2d"); 113 | window.myBar = new Chart(chart2).Bar(barChartData, { 114 | responsive : true 115 | }); 116 | var chart3 = document.getElementById("doughnut-chart").getContext("2d"); 117 | window.myDoughnut = new Chart(chart3).Doughnut(doughnutData, {responsive : true 118 | }); 119 | var chart4 = document.getElementById("pie-chart").getContext("2d"); 120 | window.myPie = new Chart(chart4).Pie(pieData, {responsive : true 121 | }); 122 | 123 | }; -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/js/easypiechart-data.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('#easypiechart-teal').easyPieChart({ 3 | scaleColor: false, 4 | barColor: '#1ebfae' 5 | }); 6 | }); 7 | 8 | $(function() { 9 | $('#easypiechart-orange').easyPieChart({ 10 | scaleColor: false, 11 | barColor: '#ffb53e' 12 | }); 13 | }); 14 | 15 | $(function() { 16 | $('#easypiechart-red').easyPieChart({ 17 | scaleColor: false, 18 | barColor: '#f9243f' 19 | }); 20 | }); 21 | 22 | $(function() { 23 | $('#easypiechart-blue').easyPieChart({ 24 | scaleColor: false, 25 | barColor: '#30a5ff' 26 | }); 27 | }); 28 | 29 | $('#calendar').datepicker({ 30 | }); 31 | -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document); -------------------------------------------------------------------------------- /lab2-dos-resource-exhaustion/static/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill 2 | * Copyright 2014 Scott Jehl 3 | * Licensed under MIT 4 | * http://j.mp/respondjs */ 5 | 6 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b 2 | 3 | 4 | 5 | 6 | 7 | GraphQL - Resource Exahustion 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 38 | 39 | 47 | 48 |
49 |
50 | 51 |
52 | 53 |
54 |
55 |

56 |
57 |
58 | 59 | 60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 89 | 90 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /lab3-mutation/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.7 2 | MAINTAINER Davide Cioccia 3 | RUN apk update --no-cache && apk add python3 \ 4 | python3-dev \ 5 | py3-pip \ 6 | bash \ 7 | gcc \ 8 | musl-dev \ 9 | g++ 10 | 11 | COPY . /app 12 | WORKDIR /app 13 | RUN pip3 install -r requirements.txt 14 | RUN python3 populate-database.py 15 | CMD [ "python3", "app.py"] -------------------------------------------------------------------------------- /lab3-mutation/README.md: -------------------------------------------------------------------------------- 1 | # Mutation 2 | 3 | 4 | The application uses GraphQL to create content on our blog, using mutations 5 | 6 | > Run the application 7 | 8 | ```sh 9 | docker build . -t graphql/mutation && docker run -ti -p 5000:5000 graphql/mutation 10 | ``` 11 | 12 | Great now the app is running. Browse to `http://0.0.0.0:5000/` 13 | 14 | 15 | 16 | ## Discovery 17 | 18 | The application implements a very basic mutation to create a new post on the blog. The mutation used is the following 19 | 20 | ``` 21 | 22 | mutation { 23 | createPost(input: {body: "' -- ", title: "test_title", authorId: 2}) { 24 | post { 25 | body 26 | authorId 27 | title 28 | } 29 | } 30 | } 31 | 32 | ``` 33 | 34 | If we look at the code we have a class `CreatePost` that will implement our logic to create a post. 35 | 36 | ``` 37 | class CreatePost(graphene.Mutation): 38 | """Mutation to create a post.""" 39 | post = graphene.Field(lambda: PostObject, description="Post created by this mutation.") 40 | 41 | class Arguments: 42 | 43 | input = CreatePostInput(required=True) 44 | 45 | def mutate(self, info, input): 46 | 47 | post = Post(**input) 48 | db.session.add(post) 49 | db.session.commit() 50 | 51 | return CreatePost(post=post) 52 | ``` 53 | 54 | The method mutate will just get the new Post object and insert an instance in the database. 55 | 56 | ## Exploit 57 | 58 | > What can you exploit? ;) 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /lab3-mutation/app.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | from flask import Flask, render_template 3 | from flask_sqlalchemy import SQLAlchemy 4 | import os 5 | import graphene 6 | from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField 7 | from flask_graphql import GraphQLView 8 | 9 | 10 | 11 | basedir = os.path.abspath(os.path.dirname(__file__)) 12 | 13 | 14 | # app initialization 15 | try: 16 | app = Flask(__name__, static_url_path='/static', static_folder='static') 17 | app.debug = True 18 | except GraphQLError as gqle: 19 | raise gqle 20 | 21 | 22 | # Configs 23 | 24 | ## Configuring the database 25 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite') 26 | app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True 27 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True 28 | 29 | # Modules 30 | 31 | ## Declaring the database 32 | db = SQLAlchemy(app) 33 | 34 | 35 | # Models 36 | 37 | 38 | # 1 User -> N Post 39 | # 40 | # 1 Post -> 1 User 41 | # 42 | 43 | 44 | class User(db.Model): 45 | __tablename__ = 'users' 46 | uuid = db.Column(db.Integer, primary_key=True) 47 | username = db.Column(db.String(256), index=True, unique=True) 48 | posts = db.relationship('Post', backref='author') ## HERE is the problem, that enables to create recursive queries 49 | 50 | def __repr__(self): 51 | return '' % self.username 52 | class Post(db.Model): 53 | __tablename__ = 'posts' 54 | uuid = db.Column(db.Integer, primary_key=True) 55 | title = db.Column(db.String(256), index=True) 56 | body = db.Column(db.Text) 57 | author_id = db.Column(db.Integer, db.ForeignKey('users.uuid')) 58 | #author = db.relationship('User', backref='post') 59 | 60 | def __repr__(self): 61 | return '' % self.title 62 | 63 | # Schema Objects 64 | 65 | class PostObject(SQLAlchemyObjectType): 66 | class Meta: 67 | model = Post 68 | interfaces = (graphene.relay.Node, ) 69 | class UserObject(SQLAlchemyObjectType): 70 | class Meta: 71 | model = User 72 | interfaces = (graphene.relay.Node, ) 73 | 74 | 75 | 76 | class PostAttribute: 77 | title = graphene.String(description="Title of the post") 78 | body = graphene.String(description="Description") 79 | author_id = graphene.Int() 80 | 81 | 82 | class CreatePostInput(graphene.InputObjectType, PostAttribute): 83 | """Arguments to create a post.""" 84 | pass 85 | 86 | 87 | class CreatePost(graphene.Mutation): 88 | """Mutation to create a post.""" 89 | post = graphene.Field(lambda: PostObject, description="Post created by this mutation.") 90 | 91 | class Arguments: 92 | 93 | input = CreatePostInput(required=True) 94 | 95 | def mutate(self, info, input): 96 | 97 | post = Post(**input) 98 | db.session.add(post) 99 | db.session.commit() 100 | 101 | return CreatePost(post=post) 102 | 103 | #4 104 | class Mutation(graphene.ObjectType): 105 | create_post = CreatePost.Field() 106 | 107 | 108 | class Query(graphene.ObjectType): 109 | node = graphene.relay.Node.Field() 110 | all_posts = SQLAlchemyConnectionField(PostObject) 111 | all_users = SQLAlchemyConnectionField(UserObject) 112 | 113 | schema = graphene.Schema(query=Query,mutation=Mutation) 114 | 115 | 116 | # Routes 117 | 118 | app.add_url_rule( 119 | '/graphql', 120 | view_func=GraphQLView.as_view( 121 | 'graphql', 122 | schema=schema, 123 | graphiql=True # for having the GraphiQL interface 124 | ) 125 | ) 126 | 127 | @app.route('/') 128 | def index(): 129 | 130 | #Implement logic to retrieve posts 131 | 132 | return render_template("index.html" ) 133 | 134 | 135 | if __name__ == '__main__': 136 | app.run('0.0.0.0') 137 | -------------------------------------------------------------------------------- /lab3-mutation/populate-database.py: -------------------------------------------------------------------------------- 1 | from app import db, User, Post 2 | db.create_all() 3 | john = User(username='johndoe') 4 | post = Post() 5 | post.title = "Hello World" 6 | post.body = "This is the first post of jhon" 7 | post.author = john 8 | db.session.add(post) 9 | db.session.add(john) 10 | jim = User(username='jimcarry') 11 | db.session.add(jim) 12 | post2 = Post() 13 | post2.title = "Woooow" 14 | post2.body = "I'm the maaaaask" 15 | post2.author = jim 16 | db.session.add(post2) 17 | 18 | 19 | db.session.commit() 20 | print(User.query.all()) 21 | print(Post.query.all()) 22 | 23 | -------------------------------------------------------------------------------- /lab3-mutation/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask-graphql 3 | flask-migrate 4 | flask-sqlalchemy 5 | graphql-core==2.2.1 6 | graphene==2.1.8 7 | graphene-sqlalchemy -------------------------------------------------------------------------------- /lab3-mutation/static/css/bootstrap-table.css: -------------------------------------------------------------------------------- 1 | .table { 2 | margin-bottom: 0 !important; 3 | border-bottom: 1px solid #dddddd; 4 | border-collapse: collapse !important; 5 | border-radius: 1px; 6 | } 7 | 8 | .fixed-table-container { 9 | position: relative; 10 | clear: both; 11 | border: 1px solid #dddddd; 12 | border-radius: 4px; 13 | -webkit-border-radius: 4px; 14 | -moz-border-radius: 4px; 15 | } 16 | 17 | .fixed-table-header { 18 | overflow: hidden; 19 | border-radius: 4px 4px 0 0; 20 | -webkit-border-radius: 4px 4px 0 0; 21 | -moz-border-radius: 4px 4px 0 0; 22 | } 23 | 24 | .fixed-table-body { 25 | overflow-x: auto; 26 | overflow-y: auto; 27 | height: 100%; 28 | } 29 | 30 | .fixed-table-container table { 31 | width: 100%; 32 | } 33 | 34 | .fixed-table-container thead th { 35 | height: 0; 36 | padding: 0; 37 | margin: 0; 38 | border-left: 1px solid #dddddd; 39 | } 40 | 41 | .fixed-table-container thead th:first-child { 42 | border-left: none; 43 | } 44 | 45 | .fixed-table-container thead th .th-inner { 46 | padding: 8px; 47 | line-height: 24px; 48 | vertical-align: top; 49 | overflow: hidden; 50 | text-overflow: ellipsis; 51 | white-space: nowrap; 52 | } 53 | 54 | .fixed-table-container thead th .sortable { 55 | cursor: pointer; 56 | } 57 | 58 | .fixed-table-container tbody td { 59 | border-left: 1px solid #dddddd; 60 | } 61 | 62 | .fixed-table-container tbody tr:first-child td { 63 | border-top: none; 64 | } 65 | 66 | .fixed-table-container tbody td:first-child { 67 | border-left: none; 68 | } 69 | 70 | /* the same color with .active */ 71 | .fixed-table-container tbody .selected td { 72 | background-color: #f5f5f5; 73 | } 74 | 75 | .fixed-table-container .bs-checkbox { 76 | text-align: center; 77 | } 78 | 79 | .fixed-table-container .bs-checkbox .th-inner { 80 | padding: 8px 0; 81 | } 82 | 83 | .fixed-table-container input[type="radio"], 84 | .fixed-table-container input[type="checkbox"] { 85 | margin: 0 auto !important; 86 | } 87 | 88 | .fixed-table-container .no-records-found { 89 | text-align: center; 90 | } 91 | 92 | 93 | .fixed-table-pagination .pagination, 94 | .fixed-table-pagination .pagination-detail { 95 | margin-top: 10px; 96 | margin-bottom: 10px; 97 | } 98 | 99 | .fixed-table-pagination .pagination a { 100 | padding: 6px 12px; 101 | line-height: 1.428571429; 102 | } 103 | 104 | .fixed-table-pagination .pagination-info { 105 | line-height: 34px; 106 | margin-right: 5px; 107 | } 108 | 109 | .fixed-table-pagination .btn-group { 110 | position: relative; 111 | display: inline-block; 112 | vertical-align: middle; 113 | } 114 | 115 | .fixed-table-pagination .dropup .dropdown-menu { 116 | margin-bottom: 0; 117 | } 118 | 119 | .fixed-table-pagination .page-list { 120 | display: inline-block; 121 | } 122 | 123 | .fixed-table-toolbar .columns { 124 | margin-left: 5px; 125 | } 126 | 127 | .fixed-table-toolbar .columns label { 128 | display: block; 129 | padding: 3px 20px; 130 | clear: both; 131 | font-weight: normal; 132 | line-height: 1.428571429; 133 | } 134 | 135 | .fixed-table-toolbar .bars, 136 | .fixed-table-toolbar .search, 137 | .fixed-table-toolbar .columns { 138 | position: relative; 139 | margin-top: 10px; 140 | margin-bottom: 10px; 141 | line-height: 34px; 142 | } 143 | 144 | .fixed-table-pagination li.disabled a { 145 | pointer-events: none; 146 | cursor: default; 147 | } 148 | 149 | .fixed-table-loading { 150 | display: none; 151 | position: absolute; 152 | top: 42px; 153 | right: 0; 154 | bottom: 0; 155 | left: 0; 156 | z-index: 99; 157 | background-color: #fff; 158 | text-align: center; 159 | } 160 | 161 | .fixed-table-body .card-view .title { 162 | font-weight: bold; 163 | display: inline-block; 164 | min-width: 30%; 165 | text-align: left !important; 166 | } 167 | 168 | /* support bootstrap 2 */ 169 | .fixed-table-body thead th .th-inner { 170 | box-sizing: border-box; 171 | } 172 | 173 | .table th, .table td { 174 | vertical-align: middle; 175 | box-sizing: border-box; 176 | } 177 | 178 | .fixed-table-toolbar .dropdown-menu { 179 | text-align: left; 180 | max-height: 300px; 181 | overflow: auto; 182 | } 183 | 184 | .fixed-table-toolbar .btn-group>.btn-group { 185 | display: inline-block; 186 | margin-left: -1px !important; 187 | } 188 | 189 | .fixed-table-toolbar .btn-group>.btn-group>.btn { 190 | border-radius: 0; 191 | } 192 | 193 | .fixed-table-toolbar .btn-group>.btn-group:first-child>.btn { 194 | border-top-left-radius: 4px; 195 | border-bottom-left-radius: 4px; 196 | } 197 | 198 | .fixed-table-toolbar .btn-group>.btn-group:last-child>.btn { 199 | border-top-right-radius: 4px; 200 | border-bottom-right-radius: 4px; 201 | } 202 | 203 | .table>thead>tr>th { 204 | vertical-align: bottom; 205 | border-bottom: 2px solid #ddd; 206 | } 207 | 208 | /* support bootstrap 3 */ 209 | .table thead>tr>th { 210 | padding: 0; 211 | margin: 0; 212 | } 213 | 214 | .pull-right .dropdown-menu { 215 | right: 0; 216 | left: auto; 217 | } 218 | 219 | /* calculate scrollbar width */ 220 | p.fixed-table-scroll-inner { 221 | width: 100%; 222 | height: 200px; 223 | } 224 | 225 | div.fixed-table-scroll-outer { 226 | top: 0; 227 | left: 0; 228 | visibility: hidden; 229 | width: 200px; 230 | height: 150px; 231 | overflow: hidden; 232 | } -------------------------------------------------------------------------------- /lab3-mutation/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab3-mutation/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /lab3-mutation/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab3-mutation/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /lab3-mutation/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab3-mutation/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /lab3-mutation/static/img/GraphQL - Resource Exahustion_files/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab3-mutation/static/img/GraphQL - Resource Exahustion_files/avatar.png -------------------------------------------------------------------------------- /lab3-mutation/static/img/GraphQL - Resource Exahustion_files/chart-data.js: -------------------------------------------------------------------------------- 1 | var randomScalingFactor = function(){ return Math.round(Math.random()*1000)}; 2 | 3 | var lineChartData = { 4 | labels : ["January","February","March","April","May","June","July"], 5 | datasets : [ 6 | { 7 | label: "My First dataset", 8 | fillColor : "rgba(220,220,220,0.2)", 9 | strokeColor : "rgba(220,220,220,1)", 10 | pointColor : "rgba(220,220,220,1)", 11 | pointStrokeColor : "#fff", 12 | pointHighlightFill : "#fff", 13 | pointHighlightStroke : "rgba(220,220,220,1)", 14 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 15 | }, 16 | { 17 | label: "My Second dataset", 18 | fillColor : "rgba(48, 164, 255, 0.2)", 19 | strokeColor : "rgba(48, 164, 255, 1)", 20 | pointColor : "rgba(48, 164, 255, 1)", 21 | pointStrokeColor : "#fff", 22 | pointHighlightFill : "#fff", 23 | pointHighlightStroke : "rgba(48, 164, 255, 1)", 24 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 25 | } 26 | ] 27 | 28 | } 29 | 30 | var barChartData = { 31 | labels : ["January","February","March","April","May","June","July"], 32 | datasets : [ 33 | { 34 | fillColor : "rgba(220,220,220,0.5)", 35 | strokeColor : "rgba(220,220,220,0.8)", 36 | highlightFill: "rgba(220,220,220,0.75)", 37 | highlightStroke: "rgba(220,220,220,1)", 38 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 39 | }, 40 | { 41 | fillColor : "rgba(48, 164, 255, 0.2)", 42 | strokeColor : "rgba(48, 164, 255, 0.8)", 43 | highlightFill : "rgba(48, 164, 255, 0.75)", 44 | highlightStroke : "rgba(48, 164, 255, 1)", 45 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 46 | } 47 | ] 48 | 49 | } 50 | 51 | var pieData = [ 52 | { 53 | value: 300, 54 | color:"#30a5ff", 55 | highlight: "#62b9fb", 56 | label: "Blue" 57 | }, 58 | { 59 | value: 50, 60 | color: "#ffb53e", 61 | highlight: "#fac878", 62 | label: "Orange" 63 | }, 64 | { 65 | value: 100, 66 | color: "#1ebfae", 67 | highlight: "#3cdfce", 68 | label: "Teal" 69 | }, 70 | { 71 | value: 120, 72 | color: "#f9243f", 73 | highlight: "#f6495f", 74 | label: "Red" 75 | } 76 | 77 | ]; 78 | 79 | var doughnutData = [ 80 | { 81 | value: 300, 82 | color:"#30a5ff", 83 | highlight: "#62b9fb", 84 | label: "Blue" 85 | }, 86 | { 87 | value: 50, 88 | color: "#ffb53e", 89 | highlight: "#fac878", 90 | label: "Orange" 91 | }, 92 | { 93 | value: 100, 94 | color: "#1ebfae", 95 | highlight: "#3cdfce", 96 | label: "Teal" 97 | }, 98 | { 99 | value: 120, 100 | color: "#f9243f", 101 | highlight: "#f6495f", 102 | label: "Red" 103 | } 104 | 105 | ]; 106 | 107 | window.onload = function(){ 108 | var chart1 = document.getElementById("line-chart").getContext("2d"); 109 | window.myLine = new Chart(chart1).Line(lineChartData, { 110 | responsive: true 111 | }); 112 | var chart2 = document.getElementById("bar-chart").getContext("2d"); 113 | window.myBar = new Chart(chart2).Bar(barChartData, { 114 | responsive : true 115 | }); 116 | var chart3 = document.getElementById("doughnut-chart").getContext("2d"); 117 | window.myDoughnut = new Chart(chart3).Doughnut(doughnutData, {responsive : true 118 | }); 119 | var chart4 = document.getElementById("pie-chart").getContext("2d"); 120 | window.myPie = new Chart(chart4).Pie(pieData, {responsive : true 121 | }); 122 | 123 | }; -------------------------------------------------------------------------------- /lab3-mutation/static/img/GraphQL - Resource Exahustion_files/easypiechart-data.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('#easypiechart-teal').easyPieChart({ 3 | scaleColor: false, 4 | barColor: '#1ebfae' 5 | }); 6 | }); 7 | 8 | $(function() { 9 | $('#easypiechart-orange').easyPieChart({ 10 | scaleColor: false, 11 | barColor: '#ffb53e' 12 | }); 13 | }); 14 | 15 | $(function() { 16 | $('#easypiechart-red').easyPieChart({ 17 | scaleColor: false, 18 | barColor: '#f9243f' 19 | }); 20 | }); 21 | 22 | $(function() { 23 | $('#easypiechart-blue').easyPieChart({ 24 | scaleColor: false, 25 | barColor: '#30a5ff' 26 | }); 27 | }); 28 | 29 | $('#calendar').datepicker({ 30 | }); 31 | -------------------------------------------------------------------------------- /lab3-mutation/static/img/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab3-mutation/static/img/avatar.png -------------------------------------------------------------------------------- /lab3-mutation/static/img/badge.svg: -------------------------------------------------------------------------------- 1 | chatchaton gitteron gitter -------------------------------------------------------------------------------- /lab3-mutation/static/img/bones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab3-mutation/static/img/bones.png -------------------------------------------------------------------------------- /lab3-mutation/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /lab3-mutation/static/js/chart-data.js: -------------------------------------------------------------------------------- 1 | var randomScalingFactor = function(){ return Math.round(Math.random()*1000)}; 2 | 3 | var lineChartData = { 4 | labels : ["January","February","March","April","May","June","July"], 5 | datasets : [ 6 | { 7 | label: "My First dataset", 8 | fillColor : "rgba(220,220,220,0.2)", 9 | strokeColor : "rgba(220,220,220,1)", 10 | pointColor : "rgba(220,220,220,1)", 11 | pointStrokeColor : "#fff", 12 | pointHighlightFill : "#fff", 13 | pointHighlightStroke : "rgba(220,220,220,1)", 14 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 15 | }, 16 | { 17 | label: "My Second dataset", 18 | fillColor : "rgba(48, 164, 255, 0.2)", 19 | strokeColor : "rgba(48, 164, 255, 1)", 20 | pointColor : "rgba(48, 164, 255, 1)", 21 | pointStrokeColor : "#fff", 22 | pointHighlightFill : "#fff", 23 | pointHighlightStroke : "rgba(48, 164, 255, 1)", 24 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 25 | } 26 | ] 27 | 28 | } 29 | 30 | var barChartData = { 31 | labels : ["January","February","March","April","May","June","July"], 32 | datasets : [ 33 | { 34 | fillColor : "rgba(220,220,220,0.5)", 35 | strokeColor : "rgba(220,220,220,0.8)", 36 | highlightFill: "rgba(220,220,220,0.75)", 37 | highlightStroke: "rgba(220,220,220,1)", 38 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 39 | }, 40 | { 41 | fillColor : "rgba(48, 164, 255, 0.2)", 42 | strokeColor : "rgba(48, 164, 255, 0.8)", 43 | highlightFill : "rgba(48, 164, 255, 0.75)", 44 | highlightStroke : "rgba(48, 164, 255, 1)", 45 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 46 | } 47 | ] 48 | 49 | } 50 | 51 | var pieData = [ 52 | { 53 | value: 300, 54 | color:"#30a5ff", 55 | highlight: "#62b9fb", 56 | label: "Blue" 57 | }, 58 | { 59 | value: 50, 60 | color: "#ffb53e", 61 | highlight: "#fac878", 62 | label: "Orange" 63 | }, 64 | { 65 | value: 100, 66 | color: "#1ebfae", 67 | highlight: "#3cdfce", 68 | label: "Teal" 69 | }, 70 | { 71 | value: 120, 72 | color: "#f9243f", 73 | highlight: "#f6495f", 74 | label: "Red" 75 | } 76 | 77 | ]; 78 | 79 | var doughnutData = [ 80 | { 81 | value: 300, 82 | color:"#30a5ff", 83 | highlight: "#62b9fb", 84 | label: "Blue" 85 | }, 86 | { 87 | value: 50, 88 | color: "#ffb53e", 89 | highlight: "#fac878", 90 | label: "Orange" 91 | }, 92 | { 93 | value: 100, 94 | color: "#1ebfae", 95 | highlight: "#3cdfce", 96 | label: "Teal" 97 | }, 98 | { 99 | value: 120, 100 | color: "#f9243f", 101 | highlight: "#f6495f", 102 | label: "Red" 103 | } 104 | 105 | ]; 106 | 107 | window.onload = function(){ 108 | var chart1 = document.getElementById("line-chart").getContext("2d"); 109 | window.myLine = new Chart(chart1).Line(lineChartData, { 110 | responsive: true 111 | }); 112 | var chart2 = document.getElementById("bar-chart").getContext("2d"); 113 | window.myBar = new Chart(chart2).Bar(barChartData, { 114 | responsive : true 115 | }); 116 | var chart3 = document.getElementById("doughnut-chart").getContext("2d"); 117 | window.myDoughnut = new Chart(chart3).Doughnut(doughnutData, {responsive : true 118 | }); 119 | var chart4 = document.getElementById("pie-chart").getContext("2d"); 120 | window.myPie = new Chart(chart4).Pie(pieData, {responsive : true 121 | }); 122 | 123 | }; -------------------------------------------------------------------------------- /lab3-mutation/static/js/easypiechart-data.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('#easypiechart-teal').easyPieChart({ 3 | scaleColor: false, 4 | barColor: '#1ebfae' 5 | }); 6 | }); 7 | 8 | $(function() { 9 | $('#easypiechart-orange').easyPieChart({ 10 | scaleColor: false, 11 | barColor: '#ffb53e' 12 | }); 13 | }); 14 | 15 | $(function() { 16 | $('#easypiechart-red').easyPieChart({ 17 | scaleColor: false, 18 | barColor: '#f9243f' 19 | }); 20 | }); 21 | 22 | $(function() { 23 | $('#easypiechart-blue').easyPieChart({ 24 | scaleColor: false, 25 | barColor: '#30a5ff' 26 | }); 27 | }); 28 | 29 | $('#calendar').datepicker({ 30 | }); 31 | -------------------------------------------------------------------------------- /lab3-mutation/static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document); -------------------------------------------------------------------------------- /lab3-mutation/static/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill 2 | * Copyright 2014 Scott Jehl 3 | * Licensed under MIT 4 | * http://j.mp/respondjs */ 5 | 6 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b 2 | 3 | 4 | 5 | 6 | 7 | GraphQL - First Mutation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 38 | 39 | 47 | 48 |
49 |
50 | 51 |
52 | 53 |
54 |
55 |

56 |
57 |
58 | 59 | 60 | 61 | 62 |
63 |
64 | 65 | 66 | 67 |
68 | 69 | 70 |
71 | 72 |
73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 96 | 97 | 139 | 140 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /lab4-IDOR/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.7 2 | MAINTAINER Davide Cioccia 3 | RUN apk update --no-cache && apk add python3 \ 4 | python3-dev \ 5 | py3-pip \ 6 | bash \ 7 | gcc \ 8 | musl-dev \ 9 | g++ 10 | 11 | COPY . /app 12 | WORKDIR /app 13 | RUN pip3 install -r requirements.txt 14 | RUN python3 populate-database.py 15 | CMD [ "python3", "app.py"] -------------------------------------------------------------------------------- /lab4-IDOR/README.md: -------------------------------------------------------------------------------- 1 | # GraphQL IDOR 2 | 3 | The application uses GraphQL to retrieve Users and Posts for the defdev.eu new blog. 4 | 5 | > Run the application 6 | 7 | ```sh 8 | docker build . -t graphql/idor && docker run -ti -p 5000:5000 graphql/idor 9 | ``` 10 | 11 | Great now the app is running. Browse to `http://0.0.0.0:5000/` 12 | 13 | ## Discovery 14 | 15 | As soon as we browse on `http://0.0.0.0:5000` we see that now our app implements a login screen to support multiple users 16 | 17 | 18 | 19 | We already registered a user , the famous Jhon Doe and we can authneticate with the credentials 20 | 21 | ``` 22 | jhondoe 23 | password1 24 | 25 | ``` 26 | 27 | Now that we are in, we discover that every time a user logs in, the application sets a cookie called 28 | 29 | `X-Api-Key` 30 | 31 | that is used by the app to authenticate the keep the session active and recognize the user. 32 | 33 | The API key can be used also to retrieve info from the blog. But this is another story. 34 | 35 | 36 | What we want to do, is to find a way to authenticate us other users using an IDOR vulnerability. 37 | 38 | 39 | ## Exploitation 40 | 41 | What's new in this application is that an user can see his settings, browsing to the page: 42 | 43 | `http://0.0.0.0:5000/settings` 44 | 45 | of course only if authenticated. But how does this page retrieve the information is crucial. If we intercept the traffic we can see that the application sends a GraphQL query to the backend to ask for the information we see in the page. The query looks like this: 46 | 47 | ``` 48 | { 49 | singleUser (user: 1) { 50 | apiKey 51 | name 52 | surname 53 | dateOfBirth 54 | } 55 | } 56 | 57 | ``` 58 | 59 | > Knowing that, use the IDOR vulnerability to authenticate as another user 60 | 61 | 62 | 63 | ## Fix 64 | 65 | Implement authorization on graphql endpoint. Although authenticated users could query the information, you should validate that the requestor of the information is actually the legit one, and use UUID instead of ID as Int. 66 | 67 | 68 | -------------------------------------------------------------------------------- /lab4-IDOR/app.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | from flask import Flask, request,render_template, render_template_string, make_response, redirect, session 3 | from flask_sqlalchemy import SQLAlchemy 4 | import os 5 | import graphene 6 | from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField 7 | from flask_graphql import GraphQLView 8 | from flask_httpauth import HTTPBasicAuth 9 | 10 | 11 | 12 | auth = HTTPBasicAuth() 13 | 14 | basedir = os.path.abspath(os.path.dirname(__file__)) 15 | 16 | 17 | # app initialization 18 | app = Flask(__name__, static_url_path='/static', static_folder='static') 19 | app.secret_key = "SUPERSECRETKEY" 20 | app.debug = True 21 | 22 | # Configs 23 | 24 | ## Configuring the database 25 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite') 26 | app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True 27 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True 28 | 29 | # Modules 30 | 31 | ## Declaring the database 32 | db = SQLAlchemy(app) 33 | 34 | # Models 35 | 36 | 37 | # 1 User -> N Post 38 | # 39 | # 1 Post -> 1 User 40 | # 41 | 42 | 43 | 44 | class User(db.Model): 45 | __tablename__ = 'users' 46 | uuid = db.Column(db.Integer, primary_key=True) 47 | username = db.Column(db.String(256), index=True, unique=True) 48 | password = db.Column(db.String(256), index=True, unique=True) 49 | isAdmin = db.Column(db.Boolean) 50 | #posts = db.relationship('Post', backref='users') ## HERE is the problem, that enables to create recursive queries 51 | posts = db.relationship('Post', backref='author') 52 | 53 | def __repr__(self): 54 | return '' % self.username 55 | class Post(db.Model): 56 | __tablename__ = 'posts' 57 | uuid = db.Column(db.Integer, primary_key=True) 58 | title = db.Column(db.String(256), index=True) 59 | body = db.Column(db.Text) 60 | author_id = db.Column(db.Integer, db.ForeignKey('users.uuid')) 61 | 62 | def __repr__(self): 63 | return '' % self.title 64 | 65 | class UserInfo(db.Model): 66 | __tablename__ = 'user_info' 67 | uuid = db.Column(db.Integer, primary_key=True) 68 | name = db.Column(db.String(256), index=True) 69 | surname = db.Column(db.String(256), index=True) 70 | date_of_birth = db.Column(db.String(256), index=True) 71 | api_key = db.Column(db.String(256), index=True) 72 | user = db.Column(db.Integer, db.ForeignKey('users.uuid')) 73 | 74 | def __repr__(self): 75 | return '' % self.name 76 | 77 | 78 | 79 | 80 | 81 | # Schema Objects 82 | 83 | class PostObject(SQLAlchemyObjectType): 84 | class Meta: 85 | model = Post 86 | interfaces = (graphene.relay.Node, ) 87 | class UserObject(SQLAlchemyObjectType): 88 | class Meta: 89 | model = User 90 | exclude_fields = ('password') #this hides the password in the query for the Users 91 | filter_fields = { 92 | 'username': ['exact', 'icontains', 'istartswith'] 93 | } 94 | interfaces = (graphene.relay.Node, ) 95 | class UserInfoObject(SQLAlchemyObjectType): 96 | class Meta: 97 | model = UserInfo 98 | filter_fields = ['user'] 99 | interfaces = (graphene.relay.Node, ) 100 | 101 | 102 | class Query(graphene.ObjectType): 103 | node = graphene.relay.Node.Field() 104 | all_posts = SQLAlchemyConnectionField(PostObject) 105 | all_users = SQLAlchemyConnectionField(UserObject) 106 | #user_info = SQLAlchemyConnectionField(UserInfoObject) 107 | single_user = graphene.Field(UserInfoObject,user=graphene.Argument(type=graphene.Int,required=True)) 108 | 109 | @staticmethod 110 | def resolve_single_user(args,info,user): 111 | query = UserInfoObject.get_query(info=info) 112 | if user: 113 | query = query.filter(UserInfo.user == user) 114 | user_info = query.first() 115 | return user_info 116 | 117 | schema = graphene.Schema(query=Query) 118 | 119 | 120 | # Routes 121 | 122 | app.add_url_rule( 123 | '/graphql', 124 | view_func=GraphQLView.as_view( 125 | 'graphql', 126 | schema=schema, 127 | graphiql=True # for having the GraphiQL interface 128 | ) 129 | ) 130 | 131 | 132 | def verify_apikey(): 133 | 134 | query=db.session.query(UserInfo).filter_by(api_key=request.cookies.get("X-Api-Key")) 135 | user_info = query.first() 136 | if user_info: 137 | 138 | return user_info 139 | else: 140 | return None 141 | 142 | 143 | @app.route('/') 144 | def index(): 145 | 146 | user_info = verify_apikey() 147 | if user_info != None: 148 | user = user_info.name + " " + user_info.surname 149 | return render_template("index.html",username=user) 150 | else: 151 | return render_template("login.html") 152 | 153 | @app.route("/login", methods=['GET', 'POST']) 154 | def login(): 155 | 156 | if request.method == 'POST': 157 | 158 | username = request.form.get('username') 159 | password = request.form.get('password') 160 | query = db.session.query(User).filter_by(username=username,password=password) 161 | user = query.first() 162 | if user: 163 | #print(user.uuid) 164 | query_api_key = db.session.query(UserInfo).filter_by(user=user.uuid) 165 | user_info = query_api_key.first() 166 | #print(user_info.api_key) 167 | #session["X-Api-Key"] = user_info.api_key 168 | response = make_response(redirect('/')) 169 | response.set_cookie('X-Api-Key', user_info.api_key) 170 | response.set_cookie('uuid', str(user_info.user)) 171 | 172 | 173 | return response 174 | else: 175 | return render_template("login.html",error="username or password are not correct") 176 | 177 | 178 | 179 | # if(password == "admin" or username != "admin"): 180 | # return render_template("login.html", error = "invalid username") 181 | # if(password != "admin" or username == "admin"): 182 | # return render_template("login.html", error = "invalid password for username") 183 | if request.method == 'GET': 184 | return render_template("login.html", error = "") 185 | 186 | @app.route('/settings') 187 | def settings(): 188 | 189 | user_info = verify_apikey() 190 | if user_info != None: 191 | return render_template("settings.html",username=user_info.name) 192 | else: 193 | return render_template("login.html") 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | if __name__ == '__main__': 202 | app.run('0.0.0.0') 203 | -------------------------------------------------------------------------------- /lab4-IDOR/populate-database.py: -------------------------------------------------------------------------------- 1 | from app import db, User, Post, UserInfo 2 | db.create_all() 3 | john = User(username='johndoe', password="password1") 4 | user1 = User(username='user1', password="password10") 5 | user2 = User(username='user2', password="password100") 6 | user3 = User(username='user3', password="password11111111") 7 | user4 = User(username='user4', password="password1231") 8 | user5 = User(username='user5', password="password1444") 9 | user6 = User(username='user6', password="password111") 10 | user7 = User(username='user7', password="password12") 11 | admin = User(username='admin', password="password444555") 12 | john.isAdmin = False 13 | info_john = UserInfo(name="Jhon", surname="Doe", date_of_birth="01/09/1985", api_key="klaSKJDOA83847JSDskdjajeb39",user="1") 14 | info_user1 = UserInfo(name="Marc", surname="Zucker", date_of_birth="01/09/1985", api_key="klajd;aihd9[0jdioha",user="3") 15 | info_user2 = UserInfo(name="Fred", surname="List", date_of_birth="01/08/1983", api_key="asdkjasd8pu322d8h32d8h2iN",user="4") 16 | info_user3 = UserInfo(name="Jack", surname="Balmore", date_of_birth="01/09/1965", api_key="PGHJOUKJOK9059069J5J",user="5") 17 | info_user4 = UserInfo(name="Laura", surname="Fresca", date_of_birth="09/09/1976", api_key="KADSNDCAISDJASIOMAJSIDAJ",user="6") 18 | info_user5 = UserInfo(name="Marta", surname="London", date_of_birth="22/04/1985", api_key="XCVBCXVBVCXZCVBCX",user="7") 19 | info_user6 = UserInfo(name="Marika", surname="Kovac", date_of_birth="17/10/1985", api_key="93274146147Y3EUDSBHH",user="8") 20 | info_user7 = UserInfo(name="Alessia", surname="Panatta", date_of_birth="10/09/1988", api_key="974YD7ASDAS9DAG79TSAD",user="9") 21 | info_admin = UserInfo(name="Mika", surname="Hakkinen", date_of_birth="01/09/1982", api_key="AS7RA968GDBVDQIYILDSY7",user="10") 22 | post = Post() 23 | post.title = "Hello World" 24 | post.body = "This is the first post of jhon" 25 | post.author = john 26 | db.session.add(post) 27 | db.session.add(john) 28 | db.session.add(user1) 29 | db.session.add(user2) 30 | db.session.add(user3) 31 | db.session.add(user4) 32 | db.session.add(user5) 33 | db.session.add(user6) 34 | db.session.add(user7) 35 | db.session.add(admin) 36 | db.session.add(info_john) 37 | db.session.add(info_user1) 38 | db.session.add(info_user2) 39 | db.session.add(info_user3) 40 | db.session.add(info_user4) 41 | db.session.add(info_user5) 42 | db.session.add(info_user6) 43 | db.session.add(info_user7) 44 | db.session.add(info_admin) 45 | 46 | jim = User(username='jimcarry',password="password88") 47 | info_jim = UserInfo(name="Jim", surname="Carry", date_of_birth="22/02/1987",api_key="jakshd834oiabvut8bu298bcsiba",user="2") 48 | jim.isAdmin = True 49 | post2 = Post() 50 | post2.title = "Woooow" 51 | post2.body = "I'm the maaaaask" 52 | post2.author = jim 53 | db.session.add(post2) 54 | db.session.add(jim) 55 | db.session.add(info_jim) 56 | post3 = Post() 57 | post3.title = "Second Post Jhon" 58 | post3.body = "This is the second post of jhon" 59 | post3.author = john 60 | db.session.commit() 61 | -------------------------------------------------------------------------------- /lab4-IDOR/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask-graphql 3 | flask-migrate 4 | flask-sqlalchemy 5 | graphql-core==2.2.1 6 | graphene==2.1.8 7 | graphene-sqlalchemy 8 | flask_httpauth 9 | -------------------------------------------------------------------------------- /lab4-IDOR/static/css/bootstrap-table.css: -------------------------------------------------------------------------------- 1 | .table { 2 | margin-bottom: 0 !important; 3 | border-bottom: 1px solid #dddddd; 4 | border-collapse: collapse !important; 5 | border-radius: 1px; 6 | } 7 | 8 | .fixed-table-container { 9 | position: relative; 10 | clear: both; 11 | border: 1px solid #dddddd; 12 | border-radius: 4px; 13 | -webkit-border-radius: 4px; 14 | -moz-border-radius: 4px; 15 | } 16 | 17 | .fixed-table-header { 18 | overflow: hidden; 19 | border-radius: 4px 4px 0 0; 20 | -webkit-border-radius: 4px 4px 0 0; 21 | -moz-border-radius: 4px 4px 0 0; 22 | } 23 | 24 | .fixed-table-body { 25 | overflow-x: auto; 26 | overflow-y: auto; 27 | height: 100%; 28 | } 29 | 30 | .fixed-table-container table { 31 | width: 100%; 32 | } 33 | 34 | .fixed-table-container thead th { 35 | height: 0; 36 | padding: 0; 37 | margin: 0; 38 | border-left: 1px solid #dddddd; 39 | } 40 | 41 | .fixed-table-container thead th:first-child { 42 | border-left: none; 43 | } 44 | 45 | .fixed-table-container thead th .th-inner { 46 | padding: 8px; 47 | line-height: 24px; 48 | vertical-align: top; 49 | overflow: hidden; 50 | text-overflow: ellipsis; 51 | white-space: nowrap; 52 | } 53 | 54 | .fixed-table-container thead th .sortable { 55 | cursor: pointer; 56 | } 57 | 58 | .fixed-table-container tbody td { 59 | border-left: 1px solid #dddddd; 60 | } 61 | 62 | .fixed-table-container tbody tr:first-child td { 63 | border-top: none; 64 | } 65 | 66 | .fixed-table-container tbody td:first-child { 67 | border-left: none; 68 | } 69 | 70 | /* the same color with .active */ 71 | .fixed-table-container tbody .selected td { 72 | background-color: #f5f5f5; 73 | } 74 | 75 | .fixed-table-container .bs-checkbox { 76 | text-align: center; 77 | } 78 | 79 | .fixed-table-container .bs-checkbox .th-inner { 80 | padding: 8px 0; 81 | } 82 | 83 | .fixed-table-container input[type="radio"], 84 | .fixed-table-container input[type="checkbox"] { 85 | margin: 0 auto !important; 86 | } 87 | 88 | .fixed-table-container .no-records-found { 89 | text-align: center; 90 | } 91 | 92 | 93 | .fixed-table-pagination .pagination, 94 | .fixed-table-pagination .pagination-detail { 95 | margin-top: 10px; 96 | margin-bottom: 10px; 97 | } 98 | 99 | .fixed-table-pagination .pagination a { 100 | padding: 6px 12px; 101 | line-height: 1.428571429; 102 | } 103 | 104 | .fixed-table-pagination .pagination-info { 105 | line-height: 34px; 106 | margin-right: 5px; 107 | } 108 | 109 | .fixed-table-pagination .btn-group { 110 | position: relative; 111 | display: inline-block; 112 | vertical-align: middle; 113 | } 114 | 115 | .fixed-table-pagination .dropup .dropdown-menu { 116 | margin-bottom: 0; 117 | } 118 | 119 | .fixed-table-pagination .page-list { 120 | display: inline-block; 121 | } 122 | 123 | .fixed-table-toolbar .columns { 124 | margin-left: 5px; 125 | } 126 | 127 | .fixed-table-toolbar .columns label { 128 | display: block; 129 | padding: 3px 20px; 130 | clear: both; 131 | font-weight: normal; 132 | line-height: 1.428571429; 133 | } 134 | 135 | .fixed-table-toolbar .bars, 136 | .fixed-table-toolbar .search, 137 | .fixed-table-toolbar .columns { 138 | position: relative; 139 | margin-top: 10px; 140 | margin-bottom: 10px; 141 | line-height: 34px; 142 | } 143 | 144 | .fixed-table-pagination li.disabled a { 145 | pointer-events: none; 146 | cursor: default; 147 | } 148 | 149 | .fixed-table-loading { 150 | display: none; 151 | position: absolute; 152 | top: 42px; 153 | right: 0; 154 | bottom: 0; 155 | left: 0; 156 | z-index: 99; 157 | background-color: #fff; 158 | text-align: center; 159 | } 160 | 161 | .fixed-table-body .card-view .title { 162 | font-weight: bold; 163 | display: inline-block; 164 | min-width: 30%; 165 | text-align: left !important; 166 | } 167 | 168 | /* support bootstrap 2 */ 169 | .fixed-table-body thead th .th-inner { 170 | box-sizing: border-box; 171 | } 172 | 173 | .table th, .table td { 174 | vertical-align: middle; 175 | box-sizing: border-box; 176 | } 177 | 178 | .fixed-table-toolbar .dropdown-menu { 179 | text-align: left; 180 | max-height: 300px; 181 | overflow: auto; 182 | } 183 | 184 | .fixed-table-toolbar .btn-group>.btn-group { 185 | display: inline-block; 186 | margin-left: -1px !important; 187 | } 188 | 189 | .fixed-table-toolbar .btn-group>.btn-group>.btn { 190 | border-radius: 0; 191 | } 192 | 193 | .fixed-table-toolbar .btn-group>.btn-group:first-child>.btn { 194 | border-top-left-radius: 4px; 195 | border-bottom-left-radius: 4px; 196 | } 197 | 198 | .fixed-table-toolbar .btn-group>.btn-group:last-child>.btn { 199 | border-top-right-radius: 4px; 200 | border-bottom-right-radius: 4px; 201 | } 202 | 203 | .table>thead>tr>th { 204 | vertical-align: bottom; 205 | border-bottom: 2px solid #ddd; 206 | } 207 | 208 | /* support bootstrap 3 */ 209 | .table thead>tr>th { 210 | padding: 0; 211 | margin: 0; 212 | } 213 | 214 | .pull-right .dropdown-menu { 215 | right: 0; 216 | left: auto; 217 | } 218 | 219 | /* calculate scrollbar width */ 220 | p.fixed-table-scroll-inner { 221 | width: 100%; 222 | height: 200px; 223 | } 224 | 225 | div.fixed-table-scroll-outer { 226 | top: 0; 227 | left: 0; 228 | visibility: hidden; 229 | width: 200px; 230 | height: 150px; 231 | overflow: hidden; 232 | } -------------------------------------------------------------------------------- /lab4-IDOR/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab4-IDOR/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /lab4-IDOR/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab4-IDOR/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /lab4-IDOR/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab4-IDOR/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /lab4-IDOR/static/img/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab4-IDOR/static/img/avatar.png -------------------------------------------------------------------------------- /lab4-IDOR/static/img/badge.svg: -------------------------------------------------------------------------------- 1 | chatchaton gitteron gitter -------------------------------------------------------------------------------- /lab4-IDOR/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /lab4-IDOR/static/js/chart-data.js: -------------------------------------------------------------------------------- 1 | var randomScalingFactor = function(){ return Math.round(Math.random()*1000)}; 2 | 3 | var lineChartData = { 4 | labels : ["January","February","March","April","May","June","July"], 5 | datasets : [ 6 | { 7 | label: "My First dataset", 8 | fillColor : "rgba(220,220,220,0.2)", 9 | strokeColor : "rgba(220,220,220,1)", 10 | pointColor : "rgba(220,220,220,1)", 11 | pointStrokeColor : "#fff", 12 | pointHighlightFill : "#fff", 13 | pointHighlightStroke : "rgba(220,220,220,1)", 14 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 15 | }, 16 | { 17 | label: "My Second dataset", 18 | fillColor : "rgba(48, 164, 255, 0.2)", 19 | strokeColor : "rgba(48, 164, 255, 1)", 20 | pointColor : "rgba(48, 164, 255, 1)", 21 | pointStrokeColor : "#fff", 22 | pointHighlightFill : "#fff", 23 | pointHighlightStroke : "rgba(48, 164, 255, 1)", 24 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 25 | } 26 | ] 27 | 28 | } 29 | 30 | var barChartData = { 31 | labels : ["January","February","March","April","May","June","July"], 32 | datasets : [ 33 | { 34 | fillColor : "rgba(220,220,220,0.5)", 35 | strokeColor : "rgba(220,220,220,0.8)", 36 | highlightFill: "rgba(220,220,220,0.75)", 37 | highlightStroke: "rgba(220,220,220,1)", 38 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 39 | }, 40 | { 41 | fillColor : "rgba(48, 164, 255, 0.2)", 42 | strokeColor : "rgba(48, 164, 255, 0.8)", 43 | highlightFill : "rgba(48, 164, 255, 0.75)", 44 | highlightStroke : "rgba(48, 164, 255, 1)", 45 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 46 | } 47 | ] 48 | 49 | } 50 | 51 | var pieData = [ 52 | { 53 | value: 300, 54 | color:"#30a5ff", 55 | highlight: "#62b9fb", 56 | label: "Blue" 57 | }, 58 | { 59 | value: 50, 60 | color: "#ffb53e", 61 | highlight: "#fac878", 62 | label: "Orange" 63 | }, 64 | { 65 | value: 100, 66 | color: "#1ebfae", 67 | highlight: "#3cdfce", 68 | label: "Teal" 69 | }, 70 | { 71 | value: 120, 72 | color: "#f9243f", 73 | highlight: "#f6495f", 74 | label: "Red" 75 | } 76 | 77 | ]; 78 | 79 | var doughnutData = [ 80 | { 81 | value: 300, 82 | color:"#30a5ff", 83 | highlight: "#62b9fb", 84 | label: "Blue" 85 | }, 86 | { 87 | value: 50, 88 | color: "#ffb53e", 89 | highlight: "#fac878", 90 | label: "Orange" 91 | }, 92 | { 93 | value: 100, 94 | color: "#1ebfae", 95 | highlight: "#3cdfce", 96 | label: "Teal" 97 | }, 98 | { 99 | value: 120, 100 | color: "#f9243f", 101 | highlight: "#f6495f", 102 | label: "Red" 103 | } 104 | 105 | ]; 106 | 107 | window.onload = function(){ 108 | var chart1 = document.getElementById("line-chart").getContext("2d"); 109 | window.myLine = new Chart(chart1).Line(lineChartData, { 110 | responsive: true 111 | }); 112 | var chart2 = document.getElementById("bar-chart").getContext("2d"); 113 | window.myBar = new Chart(chart2).Bar(barChartData, { 114 | responsive : true 115 | }); 116 | var chart3 = document.getElementById("doughnut-chart").getContext("2d"); 117 | window.myDoughnut = new Chart(chart3).Doughnut(doughnutData, {responsive : true 118 | }); 119 | var chart4 = document.getElementById("pie-chart").getContext("2d"); 120 | window.myPie = new Chart(chart4).Pie(pieData, {responsive : true 121 | }); 122 | 123 | }; -------------------------------------------------------------------------------- /lab4-IDOR/static/js/easypiechart-data.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('#easypiechart-teal').easyPieChart({ 3 | scaleColor: false, 4 | barColor: '#1ebfae' 5 | }); 6 | }); 7 | 8 | $(function() { 9 | $('#easypiechart-orange').easyPieChart({ 10 | scaleColor: false, 11 | barColor: '#ffb53e' 12 | }); 13 | }); 14 | 15 | $(function() { 16 | $('#easypiechart-red').easyPieChart({ 17 | scaleColor: false, 18 | barColor: '#f9243f' 19 | }); 20 | }); 21 | 22 | $(function() { 23 | $('#easypiechart-blue').easyPieChart({ 24 | scaleColor: false, 25 | barColor: '#30a5ff' 26 | }); 27 | }); 28 | 29 | $('#calendar').datepicker({ 30 | }); 31 | -------------------------------------------------------------------------------- /lab4-IDOR/static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document); -------------------------------------------------------------------------------- /lab4-IDOR/static/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill 2 | * Copyright 2014 Scott Jehl 3 | * Licensed under MIT 4 | * http://j.mp/respondjs */ 5 | 6 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b 2 | 3 | 4 | 5 | 6 | 7 | GraphQL - Resource Exahustion 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 38 | 39 | 47 | 48 |
49 |
50 | 51 |
52 | 53 |
54 |
55 |

Hello {{username}}

56 |
57 |
58 | 59 | 60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 89 | 90 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /lab4-IDOR/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Login 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 |
34 |
35 |
36 | 55 |

{{error}}

56 |
57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /lab4-IDOR/templates/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | GraphQL - IDOR 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 38 | 39 | 47 | 48 |
49 |
50 | 51 |
52 | 53 |
54 |
55 |

{{username}} settings

56 |
57 |
58 | 59 | 60 | 61 |
62 | 63 | 64 |
65 | 66 |
67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 90 | 91 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /lab5-injections/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.7 2 | MAINTAINER Stefan Petrushevski 3 | RUN apk update --no-cache && apk add python3 \ 4 | python3-dev \ 5 | py3-pip \ 6 | bash \ 7 | gcc \ 8 | musl-dev \ 9 | g++ 10 | 11 | COPY . /app 12 | WORKDIR /app 13 | RUN pip3 install -r requirements.txt 14 | RUN python3 populate-database.py 15 | CMD [ "python3", "app.py"] -------------------------------------------------------------------------------- /lab5-injections/README.md: -------------------------------------------------------------------------------- 1 | # Injections 2 | 3 | This lab shows that GraphQL is not a silver bullet to any of the injection vulnerabilities. 4 | 5 | We will look at two of the most common ones: 6 | 7 | * OS Command Injection 8 | * Sql Injection 9 | 10 | ## Let's start 11 | > First lets run the application: 12 | 13 | 14 | ```sh 15 | docker build . -t graphql/inject && docker run -ti -p 5000:5000 graphql/inject 16 | ``` 17 | 18 | The docker should be up in no time and we should now be able to browse the application on `http://0.0.0.0:5000/` 19 | 20 | ## Discovery 21 | 22 | We still see the blog that we should be familiar by now. 23 | 24 | If you run your DirBuster against it or just manually try to guess few of the rountes you will notice the new `/admin` section of the web app. 25 | 26 | ``` 27 | http://0.0.0.0:5000/admin 28 | 29 | ``` 30 | There we have two functionalities: 31 | 32 | * Check SQL Server Status 33 | * Query User detiails 34 | 35 | Both of them look suspicious, so play around and understand what they actually do. 36 | 37 | Hint: you can also use what you have learned from the Introspection lab to understand the queries and mutations in the admin part ;) 38 | 39 | 40 | ## Exploitation 41 | 42 | Your gut feeling is right and the *Check SQL Server Status* should be vulnerable to *OS Command Injection* and *Query User detiails* should be vulnerable to *SQL injection* attacks. 43 | 44 | Let's get it on! 45 | 46 | ### OS Command Injection 47 | The *Check SQL Server Status* form has one input field where a server and a port are expected. 48 | 49 | Go about and try check if some of the well known SQL Server ports are open on the server. 50 | 51 | Probablly your first try was MySQL 3306 - 127.0.0.1:3306. Play around with few other ports and IPs and observe the *Status codes* we receive as results. 52 | 53 | Now go ahead and try to execute some other system commands, append, pipe, ... 54 | 55 | Apparently our input is being passed to a system command which gets executed and we get response code of it. By looking in the code we can see it is passed to a `os.system()` call. 56 | 57 | This makes it a bit harder as we don't see the actual output of the command but we don't need that... enter ***Blind OS Command Injection*** 58 | 59 | How about we try to a valid command that time effective and we can observe that... kind of an oracle, a Time Oracle. 60 | 61 | Let's try: `127.0.0.1:3306; sleep 5` 62 | 63 | As you have observed, the applciation took additional extra 5 seconds to respond, meaning our `sleep` command got executed. w00t, w00t! 64 | 65 | Now, it' up to you to come up with scenario how to further abuse this. 66 | 67 | ### SQL Injection 68 | Staying on the same `/admin` we also see *Query User detiails* functionality which allowes an Administrator to get information about users by providing `username` values. 69 | 70 | Go aronud find locate the GraphQL query that executes this transaction. 71 | 72 | Now try to fetch some information for valid or invalid users. How about trying some SQL Injection payloads? :) 73 | 74 | 75 | ``` 76 | Example: 77 | ' UNION SELECT uuid, username FROM users --") 78 | ``` 79 | 80 | By now we are aboslutly sure that this is an SQL Injection point and it is pretty that we are dealing with UNION style SQL Injection. 81 | Next up, let's try to find the other blog admins: 82 | ``` 83 | {getUser(username:"' UNION SELECT uuid, username, null, null FROM users WHERE isadmin = true --") { 84 | id 85 | username 86 | }} 87 | ``` 88 | 89 | Lets try to get the password of some user: 90 | ``` 91 | {getUser(username:"' UNION SELECT uuid, username, password, null FROM users WHERE username = 'johndoe' --") { 92 | id 93 | username 94 | }} 95 | ``` 96 | 97 | We face few hurdles here: 98 | * The query only returns one row (the first row) 99 | * We cannot see the `password` value as our GraphQL `UserObject` model excludes the `password` column/field 100 | ``` 101 | class UserObject(SQLAlchemyObjectType): 102 | class Meta: 103 | model = User 104 | exclude_fields = ('password') #this hides the password in the query for the Users 105 | filter_fields = { 106 | 'username': ['exact', 'icontains', 'istartswith'] 107 | } 108 | interfaces = (graphene.relay.Node, ) 109 | ``` 110 | 111 | 112 | This means we need to get the password out from the UNION SELECT in another variable that is passed back. 113 | 114 | The laziest approach, if our target is to get the password, is to return in all fields: 115 | ``` 116 | {getUser(username:"' UNION SELECT password, password, password, password FROM users WHERE username = 'johndoe' --") { 117 | id 118 | username 119 | 120 | }} 121 | ``` 122 | 123 | w00t w00t! 124 | 125 | What else can you!? 126 | 127 | 128 | ## Fix 129 | 130 | ### OS Command Injection 131 | By know you fully understand that GraphQL does not handle input validation or santization for you. It actually has nothing to do with it. So, fixing the code in this lab does not differ to any of your previous practices on fixing code for OS Command Line injection. 132 | 133 | ### SQL Injection 134 | SQL Injections have been present and known as vulnerability for such a long time which means that the mitigations are known, documented and well explained. Mitigating against SQL Injections when it comes to GraphQL powered applications are the same as for any other technology. 135 | In this particular project we use SQLAlchemy as ORM and graphane_sqlalchemy implemetation which is one common use case. However, as can be seen in our example mistakes can be made if we concatinate unsanitized, user provided string input to our SQLAlchemy queries. 136 | 137 | The solution? 138 | * Sanitize the input 139 | * Use parameterized queries 140 | * Use SQLAlchemy as described in documentation 141 | 142 | 143 | -------------------------------------------------------------------------------- /lab5-injections/app.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | from flask import Flask, request,render_template, render_template_string, make_response, redirect, session 3 | from flask_sqlalchemy import SQLAlchemy 4 | import os 5 | import graphene 6 | from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField 7 | from flask_graphql import GraphQLView 8 | from flask_httpauth import HTTPBasicAuth 9 | from sqlalchemy.sql import text 10 | 11 | 12 | auth = HTTPBasicAuth() 13 | 14 | basedir = os.path.abspath(os.path.dirname(__file__)) 15 | 16 | 17 | # app initialization 18 | app = Flask(__name__, static_url_path='/static', static_folder='static') 19 | app.secret_key = "SUPERSECRETKEY" 20 | app.debug = True 21 | 22 | # Configs 23 | 24 | ## Configuring the database 25 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite') 26 | app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True 27 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True 28 | 29 | # Modules 30 | 31 | ## Declaring the database 32 | db = SQLAlchemy(app) 33 | 34 | # Models 35 | 36 | 37 | # 1 User -> N Post 38 | # 39 | # 1 Post -> 1 User 40 | # 41 | 42 | 43 | 44 | class User(db.Model): 45 | __tablename__ = 'users' 46 | uuid = db.Column(db.Integer, primary_key=True) 47 | username = db.Column(db.String(256), index=True, unique=True) 48 | password = db.Column(db.String(256), index=True, unique=True) 49 | isAdmin = db.Column(db.Boolean) 50 | #posts = db.relationship('Post', backref='users') ## HERE is the problem, that enables to create recursive queries 51 | posts = db.relationship('Post', backref='author') 52 | 53 | def __repr__(self): 54 | return '' % self.username 55 | class Post(db.Model): 56 | __tablename__ = 'posts' 57 | uuid = db.Column(db.Integer, primary_key=True) 58 | title = db.Column(db.String(256), index=True) 59 | body = db.Column(db.Text) 60 | author_id = db.Column(db.Integer, db.ForeignKey('users.uuid')) 61 | 62 | def __repr__(self): 63 | return '' % self.title 64 | 65 | class UserInfo(db.Model): 66 | __tablename__ = 'user_info' 67 | uuid = db.Column(db.Integer, primary_key=True) 68 | name = db.Column(db.String(256), index=True) 69 | surname = db.Column(db.String(256), index=True) 70 | date_of_birth = db.Column(db.String(256), index=True) 71 | api_key = db.Column(db.String(256), index=True) 72 | user = db.Column(db.Integer, db.ForeignKey('users.uuid')) 73 | 74 | def __repr__(self): 75 | return '' % self.name 76 | 77 | 78 | 79 | 80 | 81 | # Schema Objects 82 | 83 | class PostObject(SQLAlchemyObjectType): 84 | class Meta: 85 | model = Post 86 | interfaces = (graphene.relay.Node, ) 87 | class UserObject(SQLAlchemyObjectType): 88 | class Meta: 89 | model = User 90 | exclude_fields = ('password') #this hides the password in the query for the Users 91 | filter_fields = { 92 | 'username': ['exact', 'icontains', 'istartswith'] 93 | } 94 | interfaces = (graphene.relay.Node, ) 95 | class UserInfoObject(SQLAlchemyObjectType): 96 | class Meta: 97 | model = UserInfo 98 | filter_fields = ['user'] 99 | interfaces = (graphene.relay.Node, ) 100 | 101 | 102 | class Query(graphene.ObjectType): 103 | """ query all users and posts """ 104 | node = graphene.relay.Node.Field() 105 | all_posts = SQLAlchemyConnectionField(PostObject) 106 | all_users = SQLAlchemyConnectionField(UserObject) 107 | 108 | """ query a single user""" 109 | single_user = graphene.Field(UserInfoObject,user=graphene.Argument(type=graphene.Int,required=True)) 110 | 111 | @staticmethod 112 | def resolve_single_user(args,info,user): 113 | query = UserInfoObject.get_query(info=info) 114 | if user: 115 | query = query.filter(UserInfo.user == user) 116 | user_info = query.first() 117 | return user_info 118 | 119 | """query the status of a server after ping""" 120 | is_sql_up = graphene.String(ip=graphene.String(required=True)) 121 | @staticmethod 122 | def resolve_is_sql_up(self, info, ip): 123 | #exec ping command against the IP; return status code 124 | host,port = ip.split(':') 125 | res = os.system('nc -w 3 ' + host + " " + port) 126 | return res 127 | 128 | """ query User information by username """ 129 | get_user = graphene.Field(UserObject, username=graphene.String(required=True)) 130 | @staticmethod 131 | def resolve_get_user(self, info, username): 132 | res = db.session.query(User).filter(text("username ='" + username + "'")).first() 133 | if res: 134 | return res 135 | else: 136 | return None; 137 | 138 | schema = graphene.Schema(query=Query) 139 | 140 | 141 | # Routes 142 | 143 | app.add_url_rule( 144 | '/graphql', 145 | view_func=GraphQLView.as_view( 146 | 'graphql', 147 | schema=schema, 148 | graphiql=True # for having the GraphiQL interface 149 | ) 150 | ) 151 | 152 | 153 | def verify_apikey(): 154 | 155 | query=db.session.query(UserInfo).filter_by(api_key=request.cookies.get("X-Api-Key")) 156 | user_info = query.first() 157 | if user_info: 158 | 159 | return user_info 160 | else: 161 | return None 162 | 163 | def verify_is_admin_user(id): 164 | 165 | query=db.session.query(User).filter_by(isAdmin=True).filter_by(uuid=id) 166 | user_info = query.first() 167 | if user_info: 168 | return True 169 | else: 170 | return False 171 | 172 | 173 | 174 | @app.route('/') 175 | def index(): 176 | 177 | user_info = verify_apikey() 178 | if user_info != None: 179 | user = user_info.name + " " + user_info.surname 180 | return render_template("index.html",username=user) 181 | else: 182 | return render_template("login.html") 183 | 184 | @app.route("/login", methods=['GET', 'POST']) 185 | def login(): 186 | 187 | if request.method == 'POST': 188 | username = request.form.get('username') 189 | password = request.form.get('password') 190 | query = db.session.query(User).filter_by(username=username,password=password) 191 | user = query.first() 192 | if user: 193 | query_api_key = db.session.query(UserInfo).filter_by(user=user.uuid) 194 | user_info = query_api_key.first() 195 | response = make_response(redirect('/')) 196 | response.set_cookie('X-Api-Key', user_info.api_key) 197 | response.set_cookie('uuid', str(user_info.user)) 198 | return response 199 | else: 200 | return render_template("login.html",error="username or password are not correct") 201 | 202 | 203 | 204 | if request.method == 'GET': 205 | return render_template("login.html", error = "") 206 | 207 | @app.route('/settings') 208 | def settings(): 209 | 210 | user_info = verify_apikey() 211 | if user_info != None: 212 | return render_template("settings.html",username=user_info.name) 213 | else: 214 | return render_template("login.html") 215 | 216 | @app.route('/admin') 217 | def admin(): 218 | if verify_is_admin_user(request.cookies.get("uuid")): 219 | return render_template("admin.html") 220 | else: 221 | return render_template("login.html") 222 | 223 | 224 | 225 | 226 | 227 | 228 | if __name__ == '__main__': 229 | app.run('0.0.0.0') 230 | -------------------------------------------------------------------------------- /lab5-injections/populate-database.py: -------------------------------------------------------------------------------- 1 | from app import db, User, Post, UserInfo 2 | db.create_all() 3 | john = User(username='johndoe', password="password1") 4 | user1 = User(username='user1', password="password10") 5 | user2 = User(username='user2', password="password100") 6 | user3 = User(username='user3', password="password11111111") 7 | user4 = User(username='user4', password="password1231") 8 | user5 = User(username='user5', password="password1444") 9 | user6 = User(username='user6', password="password111") 10 | user7 = User(username='user7', password="password12") 11 | admin = User(username='admin', password="password444555") 12 | admin.isAdmin = True 13 | john.isAdmin = False 14 | info_john = UserInfo(name="Jhon", surname="Doe", date_of_birth="01/09/1985", api_key="klaSKJDOA83847JSDskdjajeb39",user="1") 15 | info_user1 = UserInfo(name="Marc", surname="Zucker", date_of_birth="01/09/1985", api_key="klajd;aihd9[0jdioha",user="3") 16 | info_user2 = UserInfo(name="Fred", surname="List", date_of_birth="01/08/1983", api_key="asdkjasd8pu322d8h32d8h2iN",user="4") 17 | info_user3 = UserInfo(name="Jack", surname="Balmore", date_of_birth="01/09/1965", api_key="PGHJOUKJOK9059069J5J",user="5") 18 | info_user4 = UserInfo(name="Laura", surname="Fresca", date_of_birth="09/09/1976", api_key="KADSNDCAISDJASIOMAJSIDAJ",user="6") 19 | info_user5 = UserInfo(name="Marta", surname="London", date_of_birth="22/04/1985", api_key="XCVBCXVBVCXZCVBCX",user="7") 20 | info_user6 = UserInfo(name="Marika", surname="Kovac", date_of_birth="17/10/1985", api_key="93274146147Y3EUDSBHH",user="8") 21 | info_user7 = UserInfo(name="Alessia", surname="Panatta", date_of_birth="10/09/1988", api_key="974YD7ASDAS9DAG79TSAD",user="9") 22 | info_admin = UserInfo(name="Mika", surname="Hakkinen", date_of_birth="01/09/1982", api_key="AS7RA968GDBVDQIYILDSY7",user="10") 23 | post = Post() 24 | post.title = "Hello World" 25 | post.body = "This is the first post of jhon" 26 | post.author = john 27 | db.session.add(post) 28 | db.session.add(john) 29 | db.session.add(user1) 30 | db.session.add(user2) 31 | db.session.add(user3) 32 | db.session.add(user4) 33 | db.session.add(user5) 34 | db.session.add(user6) 35 | db.session.add(user7) 36 | db.session.add(admin) 37 | db.session.add(info_john) 38 | db.session.add(info_user1) 39 | db.session.add(info_user2) 40 | db.session.add(info_user3) 41 | db.session.add(info_user4) 42 | db.session.add(info_user5) 43 | db.session.add(info_user6) 44 | db.session.add(info_user7) 45 | db.session.add(info_admin) 46 | 47 | jim = User(username='jimcarry',password="password88") 48 | info_jim = UserInfo(name="Jim", surname="Carry", date_of_birth="22/02/1987",api_key="jakshd834oiabvut8bu298bcsiba",user="2") 49 | jim.isAdmin = True 50 | post2 = Post() 51 | post2.title = "Woooow" 52 | post2.body = "I'm the maaaaask" 53 | post2.author = jim 54 | db.session.add(post2) 55 | db.session.add(jim) 56 | db.session.add(info_jim) 57 | post3 = Post() 58 | post3.title = "Second Post Jhon" 59 | post3.body = "This is the second post of jhon" 60 | post3.author = john 61 | db.session.commit() 62 | -------------------------------------------------------------------------------- /lab5-injections/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask-graphql 3 | flask-migrate 4 | flask-sqlalchemy 5 | graphql-core==2.2.1 6 | graphene==2.1.8 7 | graphene-sqlalchemy 8 | flask_httpauth 9 | sqlalchemy 10 | -------------------------------------------------------------------------------- /lab5-injections/static/css/bootstrap-table.css: -------------------------------------------------------------------------------- 1 | .table { 2 | margin-bottom: 0 !important; 3 | border-bottom: 1px solid #dddddd; 4 | border-collapse: collapse !important; 5 | border-radius: 1px; 6 | } 7 | 8 | .fixed-table-container { 9 | position: relative; 10 | clear: both; 11 | border: 1px solid #dddddd; 12 | border-radius: 4px; 13 | -webkit-border-radius: 4px; 14 | -moz-border-radius: 4px; 15 | } 16 | 17 | .fixed-table-header { 18 | overflow: hidden; 19 | border-radius: 4px 4px 0 0; 20 | -webkit-border-radius: 4px 4px 0 0; 21 | -moz-border-radius: 4px 4px 0 0; 22 | } 23 | 24 | .fixed-table-body { 25 | overflow-x: auto; 26 | overflow-y: auto; 27 | height: 100%; 28 | } 29 | 30 | .fixed-table-container table { 31 | width: 100%; 32 | } 33 | 34 | .fixed-table-container thead th { 35 | height: 0; 36 | padding: 0; 37 | margin: 0; 38 | border-left: 1px solid #dddddd; 39 | } 40 | 41 | .fixed-table-container thead th:first-child { 42 | border-left: none; 43 | } 44 | 45 | .fixed-table-container thead th .th-inner { 46 | padding: 8px; 47 | line-height: 24px; 48 | vertical-align: top; 49 | overflow: hidden; 50 | text-overflow: ellipsis; 51 | white-space: nowrap; 52 | } 53 | 54 | .fixed-table-container thead th .sortable { 55 | cursor: pointer; 56 | } 57 | 58 | .fixed-table-container tbody td { 59 | border-left: 1px solid #dddddd; 60 | } 61 | 62 | .fixed-table-container tbody tr:first-child td { 63 | border-top: none; 64 | } 65 | 66 | .fixed-table-container tbody td:first-child { 67 | border-left: none; 68 | } 69 | 70 | /* the same color with .active */ 71 | .fixed-table-container tbody .selected td { 72 | background-color: #f5f5f5; 73 | } 74 | 75 | .fixed-table-container .bs-checkbox { 76 | text-align: center; 77 | } 78 | 79 | .fixed-table-container .bs-checkbox .th-inner { 80 | padding: 8px 0; 81 | } 82 | 83 | .fixed-table-container input[type="radio"], 84 | .fixed-table-container input[type="checkbox"] { 85 | margin: 0 auto !important; 86 | } 87 | 88 | .fixed-table-container .no-records-found { 89 | text-align: center; 90 | } 91 | 92 | 93 | .fixed-table-pagination .pagination, 94 | .fixed-table-pagination .pagination-detail { 95 | margin-top: 10px; 96 | margin-bottom: 10px; 97 | } 98 | 99 | .fixed-table-pagination .pagination a { 100 | padding: 6px 12px; 101 | line-height: 1.428571429; 102 | } 103 | 104 | .fixed-table-pagination .pagination-info { 105 | line-height: 34px; 106 | margin-right: 5px; 107 | } 108 | 109 | .fixed-table-pagination .btn-group { 110 | position: relative; 111 | display: inline-block; 112 | vertical-align: middle; 113 | } 114 | 115 | .fixed-table-pagination .dropup .dropdown-menu { 116 | margin-bottom: 0; 117 | } 118 | 119 | .fixed-table-pagination .page-list { 120 | display: inline-block; 121 | } 122 | 123 | .fixed-table-toolbar .columns { 124 | margin-left: 5px; 125 | } 126 | 127 | .fixed-table-toolbar .columns label { 128 | display: block; 129 | padding: 3px 20px; 130 | clear: both; 131 | font-weight: normal; 132 | line-height: 1.428571429; 133 | } 134 | 135 | .fixed-table-toolbar .bars, 136 | .fixed-table-toolbar .search, 137 | .fixed-table-toolbar .columns { 138 | position: relative; 139 | margin-top: 10px; 140 | margin-bottom: 10px; 141 | line-height: 34px; 142 | } 143 | 144 | .fixed-table-pagination li.disabled a { 145 | pointer-events: none; 146 | cursor: default; 147 | } 148 | 149 | .fixed-table-loading { 150 | display: none; 151 | position: absolute; 152 | top: 42px; 153 | right: 0; 154 | bottom: 0; 155 | left: 0; 156 | z-index: 99; 157 | background-color: #fff; 158 | text-align: center; 159 | } 160 | 161 | .fixed-table-body .card-view .title { 162 | font-weight: bold; 163 | display: inline-block; 164 | min-width: 30%; 165 | text-align: left !important; 166 | } 167 | 168 | /* support bootstrap 2 */ 169 | .fixed-table-body thead th .th-inner { 170 | box-sizing: border-box; 171 | } 172 | 173 | .table th, .table td { 174 | vertical-align: middle; 175 | box-sizing: border-box; 176 | } 177 | 178 | .fixed-table-toolbar .dropdown-menu { 179 | text-align: left; 180 | max-height: 300px; 181 | overflow: auto; 182 | } 183 | 184 | .fixed-table-toolbar .btn-group>.btn-group { 185 | display: inline-block; 186 | margin-left: -1px !important; 187 | } 188 | 189 | .fixed-table-toolbar .btn-group>.btn-group>.btn { 190 | border-radius: 0; 191 | } 192 | 193 | .fixed-table-toolbar .btn-group>.btn-group:first-child>.btn { 194 | border-top-left-radius: 4px; 195 | border-bottom-left-radius: 4px; 196 | } 197 | 198 | .fixed-table-toolbar .btn-group>.btn-group:last-child>.btn { 199 | border-top-right-radius: 4px; 200 | border-bottom-right-radius: 4px; 201 | } 202 | 203 | .table>thead>tr>th { 204 | vertical-align: bottom; 205 | border-bottom: 2px solid #ddd; 206 | } 207 | 208 | /* support bootstrap 3 */ 209 | .table thead>tr>th { 210 | padding: 0; 211 | margin: 0; 212 | } 213 | 214 | .pull-right .dropdown-menu { 215 | right: 0; 216 | left: auto; 217 | } 218 | 219 | /* calculate scrollbar width */ 220 | p.fixed-table-scroll-inner { 221 | width: 100%; 222 | height: 200px; 223 | } 224 | 225 | div.fixed-table-scroll-outer { 226 | top: 0; 227 | left: 0; 228 | visibility: hidden; 229 | width: 200px; 230 | height: 150px; 231 | overflow: hidden; 232 | } -------------------------------------------------------------------------------- /lab5-injections/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab5-injections/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /lab5-injections/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab5-injections/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /lab5-injections/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab5-injections/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /lab5-injections/static/img/GraphQL - Resource Exahustion_files/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab5-injections/static/img/GraphQL - Resource Exahustion_files/avatar.png -------------------------------------------------------------------------------- /lab5-injections/static/img/GraphQL - Resource Exahustion_files/chart-data.js: -------------------------------------------------------------------------------- 1 | var randomScalingFactor = function(){ return Math.round(Math.random()*1000)}; 2 | 3 | var lineChartData = { 4 | labels : ["January","February","March","April","May","June","July"], 5 | datasets : [ 6 | { 7 | label: "My First dataset", 8 | fillColor : "rgba(220,220,220,0.2)", 9 | strokeColor : "rgba(220,220,220,1)", 10 | pointColor : "rgba(220,220,220,1)", 11 | pointStrokeColor : "#fff", 12 | pointHighlightFill : "#fff", 13 | pointHighlightStroke : "rgba(220,220,220,1)", 14 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 15 | }, 16 | { 17 | label: "My Second dataset", 18 | fillColor : "rgba(48, 164, 255, 0.2)", 19 | strokeColor : "rgba(48, 164, 255, 1)", 20 | pointColor : "rgba(48, 164, 255, 1)", 21 | pointStrokeColor : "#fff", 22 | pointHighlightFill : "#fff", 23 | pointHighlightStroke : "rgba(48, 164, 255, 1)", 24 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 25 | } 26 | ] 27 | 28 | } 29 | 30 | var barChartData = { 31 | labels : ["January","February","March","April","May","June","July"], 32 | datasets : [ 33 | { 34 | fillColor : "rgba(220,220,220,0.5)", 35 | strokeColor : "rgba(220,220,220,0.8)", 36 | highlightFill: "rgba(220,220,220,0.75)", 37 | highlightStroke: "rgba(220,220,220,1)", 38 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 39 | }, 40 | { 41 | fillColor : "rgba(48, 164, 255, 0.2)", 42 | strokeColor : "rgba(48, 164, 255, 0.8)", 43 | highlightFill : "rgba(48, 164, 255, 0.75)", 44 | highlightStroke : "rgba(48, 164, 255, 1)", 45 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 46 | } 47 | ] 48 | 49 | } 50 | 51 | var pieData = [ 52 | { 53 | value: 300, 54 | color:"#30a5ff", 55 | highlight: "#62b9fb", 56 | label: "Blue" 57 | }, 58 | { 59 | value: 50, 60 | color: "#ffb53e", 61 | highlight: "#fac878", 62 | label: "Orange" 63 | }, 64 | { 65 | value: 100, 66 | color: "#1ebfae", 67 | highlight: "#3cdfce", 68 | label: "Teal" 69 | }, 70 | { 71 | value: 120, 72 | color: "#f9243f", 73 | highlight: "#f6495f", 74 | label: "Red" 75 | } 76 | 77 | ]; 78 | 79 | var doughnutData = [ 80 | { 81 | value: 300, 82 | color:"#30a5ff", 83 | highlight: "#62b9fb", 84 | label: "Blue" 85 | }, 86 | { 87 | value: 50, 88 | color: "#ffb53e", 89 | highlight: "#fac878", 90 | label: "Orange" 91 | }, 92 | { 93 | value: 100, 94 | color: "#1ebfae", 95 | highlight: "#3cdfce", 96 | label: "Teal" 97 | }, 98 | { 99 | value: 120, 100 | color: "#f9243f", 101 | highlight: "#f6495f", 102 | label: "Red" 103 | } 104 | 105 | ]; 106 | 107 | window.onload = function(){ 108 | var chart1 = document.getElementById("line-chart").getContext("2d"); 109 | window.myLine = new Chart(chart1).Line(lineChartData, { 110 | responsive: true 111 | }); 112 | var chart2 = document.getElementById("bar-chart").getContext("2d"); 113 | window.myBar = new Chart(chart2).Bar(barChartData, { 114 | responsive : true 115 | }); 116 | var chart3 = document.getElementById("doughnut-chart").getContext("2d"); 117 | window.myDoughnut = new Chart(chart3).Doughnut(doughnutData, {responsive : true 118 | }); 119 | var chart4 = document.getElementById("pie-chart").getContext("2d"); 120 | window.myPie = new Chart(chart4).Pie(pieData, {responsive : true 121 | }); 122 | 123 | }; -------------------------------------------------------------------------------- /lab5-injections/static/img/GraphQL - Resource Exahustion_files/easypiechart-data.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('#easypiechart-teal').easyPieChart({ 3 | scaleColor: false, 4 | barColor: '#1ebfae' 5 | }); 6 | }); 7 | 8 | $(function() { 9 | $('#easypiechart-orange').easyPieChart({ 10 | scaleColor: false, 11 | barColor: '#ffb53e' 12 | }); 13 | }); 14 | 15 | $(function() { 16 | $('#easypiechart-red').easyPieChart({ 17 | scaleColor: false, 18 | barColor: '#f9243f' 19 | }); 20 | }); 21 | 22 | $(function() { 23 | $('#easypiechart-blue').easyPieChart({ 24 | scaleColor: false, 25 | barColor: '#30a5ff' 26 | }); 27 | }); 28 | 29 | $('#calendar').datepicker({ 30 | }); 31 | -------------------------------------------------------------------------------- /lab5-injections/static/img/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab5-injections/static/img/avatar.png -------------------------------------------------------------------------------- /lab5-injections/static/img/badge.svg: -------------------------------------------------------------------------------- 1 | chatchaton gitteron gitter -------------------------------------------------------------------------------- /lab5-injections/static/img/bones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david3107/graphql-security-labs/3c367bbf03865548fd8dc889213e54b0f04f9fe3/lab5-injections/static/img/bones.png -------------------------------------------------------------------------------- /lab5-injections/static/js/chart-data.js: -------------------------------------------------------------------------------- 1 | var randomScalingFactor = function(){ return Math.round(Math.random()*1000)}; 2 | 3 | var lineChartData = { 4 | labels : ["January","February","March","April","May","June","July"], 5 | datasets : [ 6 | { 7 | label: "My First dataset", 8 | fillColor : "rgba(220,220,220,0.2)", 9 | strokeColor : "rgba(220,220,220,1)", 10 | pointColor : "rgba(220,220,220,1)", 11 | pointStrokeColor : "#fff", 12 | pointHighlightFill : "#fff", 13 | pointHighlightStroke : "rgba(220,220,220,1)", 14 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 15 | }, 16 | { 17 | label: "My Second dataset", 18 | fillColor : "rgba(48, 164, 255, 0.2)", 19 | strokeColor : "rgba(48, 164, 255, 1)", 20 | pointColor : "rgba(48, 164, 255, 1)", 21 | pointStrokeColor : "#fff", 22 | pointHighlightFill : "#fff", 23 | pointHighlightStroke : "rgba(48, 164, 255, 1)", 24 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 25 | } 26 | ] 27 | 28 | } 29 | 30 | var barChartData = { 31 | labels : ["January","February","March","April","May","June","July"], 32 | datasets : [ 33 | { 34 | fillColor : "rgba(220,220,220,0.5)", 35 | strokeColor : "rgba(220,220,220,0.8)", 36 | highlightFill: "rgba(220,220,220,0.75)", 37 | highlightStroke: "rgba(220,220,220,1)", 38 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 39 | }, 40 | { 41 | fillColor : "rgba(48, 164, 255, 0.2)", 42 | strokeColor : "rgba(48, 164, 255, 0.8)", 43 | highlightFill : "rgba(48, 164, 255, 0.75)", 44 | highlightStroke : "rgba(48, 164, 255, 1)", 45 | data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()] 46 | } 47 | ] 48 | 49 | } 50 | 51 | var pieData = [ 52 | { 53 | value: 300, 54 | color:"#30a5ff", 55 | highlight: "#62b9fb", 56 | label: "Blue" 57 | }, 58 | { 59 | value: 50, 60 | color: "#ffb53e", 61 | highlight: "#fac878", 62 | label: "Orange" 63 | }, 64 | { 65 | value: 100, 66 | color: "#1ebfae", 67 | highlight: "#3cdfce", 68 | label: "Teal" 69 | }, 70 | { 71 | value: 120, 72 | color: "#f9243f", 73 | highlight: "#f6495f", 74 | label: "Red" 75 | } 76 | 77 | ]; 78 | 79 | var doughnutData = [ 80 | { 81 | value: 300, 82 | color:"#30a5ff", 83 | highlight: "#62b9fb", 84 | label: "Blue" 85 | }, 86 | { 87 | value: 50, 88 | color: "#ffb53e", 89 | highlight: "#fac878", 90 | label: "Orange" 91 | }, 92 | { 93 | value: 100, 94 | color: "#1ebfae", 95 | highlight: "#3cdfce", 96 | label: "Teal" 97 | }, 98 | { 99 | value: 120, 100 | color: "#f9243f", 101 | highlight: "#f6495f", 102 | label: "Red" 103 | } 104 | 105 | ]; 106 | 107 | window.onload = function(){ 108 | var chart1 = document.getElementById("line-chart").getContext("2d"); 109 | window.myLine = new Chart(chart1).Line(lineChartData, { 110 | responsive: true 111 | }); 112 | var chart2 = document.getElementById("bar-chart").getContext("2d"); 113 | window.myBar = new Chart(chart2).Bar(barChartData, { 114 | responsive : true 115 | }); 116 | var chart3 = document.getElementById("doughnut-chart").getContext("2d"); 117 | window.myDoughnut = new Chart(chart3).Doughnut(doughnutData, {responsive : true 118 | }); 119 | var chart4 = document.getElementById("pie-chart").getContext("2d"); 120 | window.myPie = new Chart(chart4).Pie(pieData, {responsive : true 121 | }); 122 | 123 | }; -------------------------------------------------------------------------------- /lab5-injections/static/js/easypiechart-data.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('#easypiechart-teal').easyPieChart({ 3 | scaleColor: false, 4 | barColor: '#1ebfae' 5 | }); 6 | }); 7 | 8 | $(function() { 9 | $('#easypiechart-orange').easyPieChart({ 10 | scaleColor: false, 11 | barColor: '#ffb53e' 12 | }); 13 | }); 14 | 15 | $(function() { 16 | $('#easypiechart-red').easyPieChart({ 17 | scaleColor: false, 18 | barColor: '#f9243f' 19 | }); 20 | }); 21 | 22 | $(function() { 23 | $('#easypiechart-blue').easyPieChart({ 24 | scaleColor: false, 25 | barColor: '#30a5ff' 26 | }); 27 | }); 28 | 29 | $('#calendar').datepicker({ 30 | }); 31 | -------------------------------------------------------------------------------- /lab5-injections/static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document); -------------------------------------------------------------------------------- /lab5-injections/static/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill 2 | * Copyright 2014 Scott Jehl 3 | * Licensed under MIT 4 | * http://j.mp/respondjs */ 5 | 6 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b 2 | 3 | 4 | 5 | 6 | 7 | GraphQL - Admin panel 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 38 | 39 | 47 | 48 | 49 | 50 |
51 |
52 | 53 |
54 | 55 |
56 |
57 |

Adminisrator panel

58 |
59 |
60 | 61 |
62 |
63 | 64 |
65 | 66 |
67 | Check SQL Server Status: 68 |
69 | 70 |
71 | 72 |
73 |
74 | 75 |
76 | Query User detiails: 77 |
78 | 79 |
80 | 81 |
82 |
83 | 84 |
85 | 86 |
87 |

88 |
89 |
90 |
Results
91 |
92 | Results will be showed here 93 |
94 |
95 | 96 |
97 |
98 |
99 | 100 | 101 | 102 | 103 | 104 | 105 | 120 | 121 | 122 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /lab5-injections/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | GraphQL - First Mutation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 38 | 39 | 47 | 48 |
49 |
50 | 51 |
52 | 53 |
54 |
55 |

56 |
57 |
58 | 59 | 60 | 61 | 62 |
63 |
64 | 65 | 66 | 67 |
68 | 69 | 70 |
71 | 72 |
73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 96 | 97 | 139 | 140 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /lab5-injections/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Login 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 |
34 |
35 |
36 | 55 |

{{error}}

56 |
57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /lab5-injections/templates/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | GraphQL - IDOR 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 38 | 39 | 47 | 48 |
49 |
50 | 51 |
52 | 53 |
54 |
55 |

{{username}} settings

56 |
57 |
58 | 59 | 60 | 61 |
62 | 63 | 64 |
65 | 66 |
67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 90 | 91 | 161 | 162 | 163 | 164 | --------------------------------------------------------------------------------