id: graphql-detect

info:
  name: GraphQL API Detection
  author: nkxxkn,elsfa7110,ofjaaah,exceed
  severity: info
  metadata:
    max-request: 124
  tags: tech,graphql,discovery

http:
  - method: POST
    path:
      - "{{BaseURL}}{{paths}}"
    body: '{"query":"query IntrospectionQuery{__schema {queryType { name }}}"}'
    headers:
      Content-Type: application/json

    payloads:
      paths:
        - "/HyperGraphQL"
        - "/___graphql"
        - "/altair"
        - "/api/cask/graphql-playground"
        - "/api/graphql"
        - "/api/graphql/v1"
        - "/explorer"
        - "/express-graphql"
        - "/gql"
        - "/graph"
        - "/graph_cms"
        - "/graphiql"
        - "/graphiql.css"
        - "/graphiql.js"
        - "/graphiql.min.css"
        - "/graphiql.min.js"
        - "/graphiql.php"
        - "/graphiql/finland"
        - "/graphql"
        - "/graphql-console"
        - "/graphql-devtools"
        - "/graphql-explorer"
        - "/graphql-playground"
        - "/graphql-playground-html"
        - "/graphql.php"
        - "/graphql/console"
        - "/graphql/graphql-playground"
        - "/graphql/schema.json"
        - "/graphql/schema.xml"
        - "/graphql/schema.yaml"
        - "/graphql/v1"
        - "/je/graphql"
        - "/laravel-graphql-playground"
        - "/playground"
        - "/portal-graphql"
        - "/query"
        - "/query-api"
        - "/query-explorer"
        - "/query-laravel"
        - "/sphinx-graphiql"
        - "/subscriptions"
        - "/v1"
        - "/v1/altair"
        - "/v1/api/graphql"
        - "/v1/explorer"
        - "/v1/graph"
        - "/v1/graphiql"
        - "/v1/graphiql.css"
        - "/v1/graphiql.js"
        - "/v1/graphiql.min.css"
        - "/v1/graphiql.min.js"
        - "/v1/graphiql.php"
        - "/v1/graphiql/finland"
        - "/v1/graphql"
        - "/v1/graphql-explorer"
        - "/v1/graphql.php"
        - "/v1/graphql/console"
        - "/v1/graphql/schema.json"
        - "/v1/graphql/schema.xml"
        - "/v1/graphql/schema.yaml"
        - "/v1/playground"
        - "/v1/subscriptions"
        - "/v2"
        - "/v2/altair"
        - "/v2/api/graphql"
        - "/v2/explorer"
        - "/v2/graph"
        - "/v2/graphiql"
        - "/v2/graphiql.css"
        - "/v2/graphiql.js"
        - "/v2/graphiql.min.css"
        - "/v2/graphiql.min.js"
        - "/v2/graphiql.php"
        - "/v2/graphiql/finland"
        - "/v2/graphql"
        - "/v2/graphql-explorer"
        - "/v2/graphql.php"
        - "/v2/graphql/console"
        - "/v2/graphql/schema.json"
        - "/v2/graphql/schema.xml"
        - "/v2/graphql/schema.yaml"
        - "/v2/playground"
        - "/v2/subscriptions"
        - "/v3"
        - "/v3/altair"
        - "/v3/api/graphql"
        - "/v3/explorer"
        - "/v3/graph"
        - "/v3/graphiql"
        - "/v3/graphiql.css"
        - "/v3/graphiql.js"
        - "/v3/graphiql.min.css"
        - "/v3/graphiql.min.js"
        - "/v3/graphiql.php"
        - "/v3/graphiql/finland"
        - "/v3/graphql"
        - "/v3/graphql-explorer"
        - "/v3/graphql.php"
        - "/v3/graphql/console"
        - "/v3/graphql/schema.json"
        - "/v3/graphql/schema.xml"
        - "/v3/graphql/schema.yaml"
        - "/v3/playground"
        - "/v3/subscriptions"
        - "/v4/altair"
        - "/v4/api/graphql"
        - "/v4/explorer"
        - "/v4/graph"
        - "/v4/graphiql"
        - "/v4/graphiql.css"
        - "/v4/graphiql.js"
        - "/v4/graphiql.min.css"
        - "/v4/graphiql.min.js"
        - "/v4/graphiql.php"
        - "/v4/graphiql/finland"
        - "/v4/graphql"
        - "/v4/graphql-explorer"
        - "/v4/graphql.php"
        - "/v4/graphql/console"
        - "/v4/graphql/schema.json"
        - "/v4/graphql/schema.xml"
        - "/v4/graphql/schema.yaml"
        - "/v4/playground"
        - "/v4/subscriptions"

    stop-at-first-match: true
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200

      - type: word
        part: header
        words:
          - "application/json"

      - type: regex
        regex:
          - "__schema"
          - "(Introspection|INTROSPECTION|introspection).*?"
          - ".*?operation not found.*?"
        condition: or
# digest: 4a0a0047304502203d90a930654318d71a9c4360817fc8107d2a98db9f957e3d066a4930de5c79c5022100ffb5e1707f69dd95b724ba10d780129c61b219fa585639be34a7391edaa5d3f6:922c64590222798bb761d5b6d8e72950