Adding support for GraphQL (--graphql)

This commit is contained in:
Miroslav Štampar 2026-06-27 19:23:30 +02:00
parent 2893fd5c4d
commit f6912fc921
11 changed files with 2207 additions and 8 deletions

View file

@ -119,6 +119,7 @@ optDict = {
"Techniques": {
"technique": "string",
"nosql": "boolean",
"graphql": "boolean",
"timeSec": "integer",
"uCols": "string",
"uChar": "string",

View file

@ -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

View file

@ -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'",)),