mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-06-28 12:31:00 +00:00
Adding support for GraphQL (--graphql)
This commit is contained in:
parent
2893fd5c4d
commit
f6912fc921
11 changed files with 2207 additions and 8 deletions
|
|
@ -79,6 +79,7 @@ from lib.core.settings import DEFAULT_GET_POST_DELIMITER
|
|||
from lib.core.settings import DUMMY_NON_SQLI_CHECK_APPENDIX
|
||||
from lib.core.settings import FI_ERROR_REGEX
|
||||
from lib.core.settings import FORMAT_EXCEPTION_STRINGS
|
||||
from lib.core.settings import GRAPHQL_ERROR_REGEX
|
||||
from lib.core.settings import HEURISTIC_CHECK_ALPHABET
|
||||
from lib.core.settings import INFERENCE_EQUALS_CHAR
|
||||
from lib.core.settings import IPS_WAF_CHECK_PAYLOAD
|
||||
|
|
@ -1178,6 +1179,13 @@ def heuristicCheckSqlInjection(place, parameter):
|
|||
if conf.beep:
|
||||
beep()
|
||||
|
||||
if not conf.graphql and re.search(GRAPHQL_ERROR_REGEX, page or ""):
|
||||
infoMsg = "heuristic (GraphQL) test shows that %sparameter '%s' appears to be a GraphQL endpoint (rerun with switch '--graphql')" % ("%s " % paramType if paramType != parameter else "", parameter)
|
||||
logger.info(infoMsg)
|
||||
|
||||
if conf.beep:
|
||||
beep()
|
||||
|
||||
kb.disableHtmlDecoding = False
|
||||
kb.heuristicMode = False
|
||||
|
||||
|
|
|
|||
|
|
@ -504,8 +504,21 @@ def start():
|
|||
infoMsg = "testing URL '%s'" % targetUrl
|
||||
logger.info(infoMsg)
|
||||
|
||||
if conf.graphql and PLACE.GET not in conf.parameters:
|
||||
# graphqlScan() is self-contained and operates on the GraphQL
|
||||
# document, not on HTTP parameters. A dummy GET parameter keeps
|
||||
# _setRequestParams() from appending the URI injection marker ('*')
|
||||
# to a bare endpoint URL (which would break detection under
|
||||
# '--batch'); it is discarded by graphqlScan() on entry.
|
||||
conf.parameters[PLACE.GET] = "x"
|
||||
|
||||
setupTargetEnv()
|
||||
|
||||
if conf.graphql:
|
||||
from lib.techniques.graphql.inject import graphqlScan
|
||||
graphqlScan()
|
||||
continue
|
||||
|
||||
if not checkConnection(suppressOutput=conf.forms):
|
||||
continue
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ optDict = {
|
|||
"Techniques": {
|
||||
"technique": "string",
|
||||
"nosql": "boolean",
|
||||
"graphql": "boolean",
|
||||
"timeSec": "integer",
|
||||
"uCols": "string",
|
||||
"uChar": "string",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ from lib.core.enums import OS
|
|||
from thirdparty import six
|
||||
|
||||
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
||||
VERSION = "1.10.6.160"
|
||||
VERSION = "1.10.6.161"
|
||||
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
|
||||
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
|
||||
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
|
||||
|
|
@ -877,6 +877,68 @@ NOSQL_MAX_RECORDS = 100
|
|||
# Upper bound for the length search during NoSQL blind extraction
|
||||
NOSQL_MAX_LENGTH = 1024
|
||||
|
||||
# GraphQL endpoint paths to probe when the user supplies a base URL with --graphql (no explicit /graphql)
|
||||
GRAPHQL_ENDPOINT_PATHS = ("/graphql", "/api/graphql", "/v1/graphql", "/graphql/api", "/graph", "/gql")
|
||||
|
||||
# Canonical GraphQL introspection query (the one everyone copy-pastes). Returned schema carries the
|
||||
# full type system: query/mutation/subscription roots, OBJECT/INPUT_OBJECT/ENUM/SCALAR types, their
|
||||
# fields/arguments/inputFields with type chains, directives, and deprecation metadata.
|
||||
GRAPHQL_INTROSPECTION_QUERY = """query IntrospectionForSqlmap {
|
||||
__schema {
|
||||
queryType { name }
|
||||
mutationType { name }
|
||||
subscriptionType { name }
|
||||
directives { name args { name type { kind name ofType { kind name ofType { kind name } } } } }
|
||||
types {
|
||||
kind
|
||||
name
|
||||
fields(includeDeprecated: true) {
|
||||
name
|
||||
args {
|
||||
name
|
||||
defaultValue
|
||||
type { kind name ofType { kind name ofType { kind name ofType { kind name } } } }
|
||||
}
|
||||
type { kind name ofType { kind name ofType { kind name } } }
|
||||
}
|
||||
inputFields {
|
||||
name
|
||||
defaultValue
|
||||
type { kind name ofType { kind name ofType { kind name ofType { kind name } } } }
|
||||
}
|
||||
enumValues(includeDeprecated: true) { name }
|
||||
specifiedByURL
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
# GraphQL error patterns that identify the response as originating from a GraphQL layer (parse,
|
||||
# validation, execution, or APQ errors). Used by the heuristic in checks.py and for error-based
|
||||
# detection inside the GraphQL engine.
|
||||
GRAPHQL_PARSE_ERRORS = (
|
||||
r'"code"\s*:\s*"GRAPHQL_PARSE_FAILED"',
|
||||
r"\bSyntax Error:\s*[^\"]",
|
||||
r"\bExpected Name,\s*found\b",
|
||||
r"\bUnexpected\s+<EOF>\b",
|
||||
)
|
||||
GRAPHQL_VALIDATION_ERRORS = (
|
||||
r'"code"\s*:\s*"GRAPHQL_VALIDATION_FAILED"',
|
||||
r"\bCannot query field\s+\"[^\"]+\"\s+on type\s+\"[^\"]+\"",
|
||||
r"\bUnknown argument\s+\"[^\"]+\"\s+on field\s+\"[^\"]+\"",
|
||||
r"\bField\s+\"[^\"]+\"\s+argument\s+\"[^\"]+\"\s+of type\s+\"[^\"]+\"\s+is required\b",
|
||||
r"\bVariable\s+\"\$[^\"]+\"\s+got invalid value\b",
|
||||
r"\bExpected type\s+[^,]+,\s*found\b",
|
||||
r"\bDid you mean\s+\"[^\"]+\"\b",
|
||||
)
|
||||
GRAPHQL_APQ_ERRORS = (
|
||||
r"\bPersistedQueryNotFound\b",
|
||||
r"\bPersistedQueryNotSupported\b",
|
||||
)
|
||||
GRAPHQL_RUNTIME_ERRORS = (
|
||||
r"\bGraphQL\s+(?:resolver\s+)?error\b",
|
||||
)
|
||||
GRAPHQL_ERROR_REGEX = "(?:%s)" % '|'.join(GRAPHQL_PARSE_ERRORS + GRAPHQL_VALIDATION_ERRORS + GRAPHQL_APQ_ERRORS + GRAPHQL_RUNTIME_ERRORS)
|
||||
|
||||
# Length of prefix and suffix used in non-SQLI heuristic checks
|
||||
NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH = 6
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ def vulnTest():
|
|||
("-u <url> -z \"tec=B\" --hex --fresh-queries --threads=4 --sql-query=\"SELECT * FROM users\"", ("SELECT * FROM users [30]", "nameisnull")),
|
||||
("-u \"<url>&echo=foobar*\" --flush-session", ("might be vulnerable to cross-site scripting",)),
|
||||
("-u \"<base>nosql?name=luther&password=x\" -p password --nosql --flush-session", ("is vulnerable to NoSQL injection", "back-end: 'MongoDB'", "NoSQL: GET parameter 'password'", "s3cr3t")), # NoSQL (MongoDB) operator-injection detection + blind regexp extraction
|
||||
("-u \"<base>graphql\" --graphql --flush-session", ("found GraphQL endpoint", "introspection returned", "skipping 2 mutation slot", "GraphQL boolean-based blind", "in-band data exposure", "back-end DBMS: 'SQLite'", "banner: '3.", "available tables [2]: users, creds", "dumped table 'creds'", "db3a16990a0008a3b04707fdef6584a0", "graphql scan complete")), # GraphQL: endpoint detection + introspection + mutation-skip + boolean-blind/in-band + back-end fingerprint + batched blind dump of an injection-only table (SQLite-backed)
|
||||
("-u \"<url>&query=*\" --flush-session --technique=Q --banner", ("Title: SQLite inline queries", "banner: '3.")),
|
||||
("-d \"<direct>\" --flush-session --dump -T creds --dump-format=SQLITE --binary-fields=password_hash --where \"user_id=5\"", ("3137396164343563366365326362393763663130323965323132303436653831", "dumped to SQLITE database")),
|
||||
("-d \"<direct>\" --flush-session --banner --schema --sql-query=\"UPDATE users SET name='foobar' WHERE id=4; SELECT * FROM users; SELECT 987654321\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "4,foobar,nameisnull", "'987654321'",)),
|
||||
|
|
|
|||
|
|
@ -418,6 +418,9 @@ def cmdLineParser(argv=None):
|
|||
techniques.add_argument("--nosql", dest="nosql", action="store_true",
|
||||
help="Test for NoSQL injection (e.g. MongoDB, CouchDB, Neo4j)")
|
||||
|
||||
techniques.add_argument("--graphql", dest="graphql", action="store_true",
|
||||
help="Test for GraphQL injection (introspection, field/argument fuzzing, SQL/NoSQL payload families)")
|
||||
|
||||
techniques.add_argument("--time-sec", dest="timeSec", type=int,
|
||||
help="Seconds to delay the DBMS response (default %d)" % defaults.timeSec)
|
||||
|
||||
|
|
|
|||
8
lib/techniques/graphql/__init__.py
Normal file
8
lib/techniques/graphql/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
|
||||
See the file 'LICENSE' for copying permission
|
||||
"""
|
||||
|
||||
pass
|
||||
1165
lib/techniques/graphql/inject.py
Normal file
1165
lib/techniques/graphql/inject.py
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue