/
graphql-introspection.nse
109 lines (89 loc) · 2.85 KB
/
graphql-introspection.nse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
description = [[
Identifies webservers running GraphQL endpoints and attempts an execution of an Introspection query for information gathering.
This script queries for common graphql endpoints and then sends an Introspection query and inspects the result.
Resources
* https://graphql.org/learn/introspection/
]]
---
-- @usage
-- nmap --script graphql-introspection.nse <target>
-- nmap -sV --script graphql-introspection <target>
--
-- @output
-- PORT STATE SERVICE REASON
-- 443/tcp open ssl/http nginx
-- | graphql:
-- |_ /graphql - is vulnerable to introspection queries!
-- |_http-server-header: Jetty(9.4.z-SNAPSHOT)
---
author = "Dolev Farhi"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "fuzzer", "vuln", "intrusive"}
local http = require "http"
local vulns = require 'vulns'
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local json = require "json"
portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open")
-- Checks if graphql is responding to an Introspection POST request
-- @param host Hostname
-- @param port Port number
-- @return True if response is 200 and contains __schema in body
local function check_introspection(host, port)
local payload = {
query = 'query IntrospectionQuery{__schema {queryType { name }}}'
}
local gql_endpoints = {
"/",
"/graphql",
"/graphiql",
"/v1/graphql",
"/v2/graphql",
"/v3/graphql",
"/graphql/console",
"/v1/graphql/console",
"/v2/graphql/console",
"/v3/graphql/console",
"/v1/graphiql",
"/v2/graphiql",
"/v3/graphiql",
"/playground",
"/query",
"/explorer",
"/altair",
}
for _, path in ipairs(gql_endpoints) do
stdnse.debug2("Checking GraphQL at Path: %s", path)
req = http.post(host, port, path ,{header = {["Content-Type"] = "application/json"}} , nil, (json.generate(payload)))
if req.status and req.status == 200 and string.find(req.body, "__schema") then
return "Endpoint: " .. path .. " is vulnerable to introspection queries!"
else
stdnse.debug2("Failed finding GraphQL at Path: %s", path)
end
end
return false
end
---
--main
---
action = function(host, port)
local vuln = {
state = vulns.STATE.NOT_VULN,
description = [[
Checks if GraphQL allows Introspection Queries.
]],
references = {
'https://graphql.org/learn/introspection/'
}
}
local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
stdnse.debug1("GraphQL Instrospection check is running...")
result = check_introspection(host, port)
if result then
stdnse.debug1("GraphQL Introspection is enabled.")
vuln.title = 'GraphQL Server allows Introspection queries at endpoint: ' .. result
vuln.state = vulns.STATE.VULN
end
return vuln_report:make_output(vuln)
end