Skip to content

Add GraphQL Auxiliary Scanner module #20216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

sjanusz-r7
Copy link
Contributor

@sjanusz-r7 sjanusz-r7 commented May 21, 2025

This PR adds a GraphQL Introspection Scanner module.
This module can be used to query GraphQL endpoints to see if introspection is enabled.
Introspection allows us to query for the whole GraphQL schema, which could give us insight into which objects can be queried, their descriptions, types, and if they are deprecated. This can be used to query for information that should have been not accessible, e.g. not exposed to a UI, but can still be queried. As such, we register it as a vulnerability.

GraphQL Server

For this, I have used two GraphQL implementations, as well as remote GraphQL endpoints available at: https://github.com/graphql-kit/graphql-apis

Remote GraphQL Instances

  • api.catalysis-hub.org
run rhost=https://api.catalysis-hub.org/graphql?
[*] Running module against 3.33.161.45
[*] 3.33.161.45:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Running module against 13.248.131.213
[*] 13.248.131.213:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Running module against 15.197.152.254
[*] 15.197.152.254:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Running module against 35.71.150.51
[*] 35.71.150.51:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Auxiliary module execution completed
  • app.pipefy.com (Seems like their GraphQL endpoint always returns an Access Denied, even from their GraphiQL UI)
msf-pro auxiliary(scanner/http/graphql_introspection_scanner) > run rhost=https://app.pipefy.com/graphql
[*] Running module against 104.19.147.54
[-] 104.19.147.54:443 - Server encountered the following error(s) (code: '401'):
  - You are not authorized to access this page
[*] Running module against 104.19.148.54
[-] 104.19.148.54:443 - Server encountered the following error(s) (code: '401'):
  - You are not authorized to access this page
[*] Auxiliary module execution completed
  • graphloc.com
msf-pro auxiliary(scanner/http/graphql_introspection_scanner) > run rhost=https://graphloc.com/
[*] Running module against 151.101.1.195
[*] 151.101.1.195:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Running module against 151.101.65.195
[*] 151.101.65.195:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Auxiliary module execution completed
  • https://docs.developer.yelp.com/graphql
    Doesn't quite work; I assume because of some missing headers, other magic values, or this is coming from the server due to routing, load balancing or others. We get a 404 Not Found.

  • api.ean-search.org

rumsf-pro auxiliary(scanner/http/graphql_introspection_scanner) > run rhost=https://api.ean-search.org/graphql
[*] Running module against 176.9.22.180
[-] 176.9.22.180:443 - Server encountered the following error(s) (code: '402'):
  - Authorization failed
[*] Auxiliary module execution completed
  • countries.trevorblades.com
    This server gets an error on the full schema dump query: Query depth limit exceeded.. As such, we can check if the host is vulnerable:
msf-pro auxiliary(scanner/http/graphql_introspection_scanner) > run rhost=https://countries.trevorblades.com/
[*] Running module against 151.101.1.51
[-] 151.101.1.51:443 - Server encountered the following error(s) (code: '413'):
  - Query depth limit exceeded.
[*] Running module against 151.101.65.51
[-] 151.101.65.51:443 - Server encountered the following error(s) (code: '413'):
  - Query depth limit exceeded.
[*] Running module against 151.101.129.51
[-] 151.101.129.51:443 - Server encountered the following error(s) (code: '413'):
  - Query depth limit exceeded.
[*] Running module against 151.101.193.51
[-] 151.101.193.51:443 - Server encountered the following error(s) (code: '413'):
  - Query depth limit exceeded.
[*] Running module against 2a04:4e42::307
[-] The host ([2a04:4e42::307]:443) was unreachable.
[-] 2a04:4e42::307:443 - The server did not send a response.
[*] Running module against 2a04:4e42:200::307
[-] The host ([2a04:4e42:200::307]:443) was unreachable.
[-] 2a04:4e42:200::307:443 - The server did not send a response.
[*] Running module against 2a04:4e42:400::307
[-] The host ([2a04:4e42:400::307]:443) was unreachable.
[-] 2a04:4e42:400::307:443 - The server did not send a response.
[*] Running module against 2a04:4e42:600::307
[-] The host ([2a04:4e42:600::307]:443) was unreachable.
[-] 2a04:4e42:600::307:443 - The server did not send a response.
[*] Auxiliary module execution completed
msf-pro auxiliary(scanner/http/graphql_introspection_scanner) > check rhost=https://countries.trevorblades.com/
[+] 151.101.1.51:443 - The target is vulnerable. The server has introspection enabled.
[+] 151.101.65.51:443 - The target is vulnerable. The server has introspection enabled.
[+] 151.101.129.51:443 - The target is vulnerable. The server has introspection enabled.
[+] 151.101.193.51:443 - The target is vulnerable. The server has introspection enabled.
[-] The host ([2a04:4e42::307]:443) was unreachable.
[*] 2a04:4e42::307:443 - Cannot reliably check exploitability. The server did not send a response.
[-] The host ([2a04:4e42:200::307]:443) was unreachable.
[*] 2a04:4e42:200::307:443 - Cannot reliably check exploitability. The server did not send a response.
[-] The host ([2a04:4e42:400::307]:443) was unreachable.
[*] 2a04:4e42:400::307:443 - Cannot reliably check exploitability. The server did not send a response.
[-] The host ([2a04:4e42:600::307]:443) was unreachable.
[*] 2a04:4e42:600::307:443 - Cannot reliably check exploitability. The server did not send a response.

JavaScript & Node.js

For setting up a JavaScript GraphQL server, I have followed this: https://www.apollographql.com/docs/apollo-server/getting-started

The following code can be pasted into a node.js project, and executed with SERVER_PORT=4000 NODE_ENV=production npm start and SERVER_PORT=4001 NODE_ENV=development npm start
Server code:

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

const books = [
    {
        title: 'The Awakening',
        author: 'Kate Chopin',
    },
    {
        title: 'City of Glass',
        author: 'Paul Auster',
    },
];

const metasploit_modules = [
    {
        name: 'WebScanner',
        authors: [ 'sjanusz-r7', 'sjanusz' ],
        cves: [ '2025-01-01' ],
        description: 'Web Scanner Module'
    }
]

// A schema is a collection of type definitions (hence "typeDefs")
// that together define the "shape" of queries that are executed against
// your data.
const typeDefs = `#graphql
  # Comments in GraphQL strings (such as this one) start with the hash (#) symbol.

  # This "Book" type defines the queryable fields for every book in our data source.
  type Book {
    title: String
    author: String
  }

  type MetasploitModule {
    name: String!,
    authors: [String],
    cves: [String],
    description: String
  }

  # The "Query" type is special: it lists all of the available queries that
  # clients can execute, along with the return type for each. In this
  # case, the "books" query returns an array of zero or more Books (defined above).
  type Query {
    books: [Book],
    metasploit_modules: [MetasploitModule]
  }
`;

// Resolvers define how to fetch the types defined in your schema.
// This resolver retrieves books from the "books" array above.
const resolvers = {
    Query: {
        books: () => books,
        metasploit_modules: () => metasploit_modules
    },
};

// The ApolloServer constructor requires two parameters: your schema
// definition and your set of resolvers.
const server = new ApolloServer({
    typeDefs,
    resolvers,
});

// Passing an ApolloServer instance to the `startStandaloneServer` function:
//  1. creates an Express app
//  2. installs your ApolloServer instance as middleware
//  3. prepares your app to handle incoming requests
const { url } = await startStandaloneServer(server, {
    listen: { port: process.env.SERVER_PORT }
});

console.log(`🚀  Server ready at: ${url}`);

Ruby & Rails

For this, I have used the following tutorial: https://www.apollographql.com/blog/using-graphql-with-ruby-on-rails

TLDR: This implementation is different. Rails uses CSRF tokens. On a more barebones app (such as the tutorial linked), there is no programatic way to retrieve a CSRF token, as there is no endpoints for that in a production env.

Verification

List the steps needed to make sure this thing works

  • Start msfconsole
  • use graphql_introspection_scanner
  • set your options
  • Make sure you can check and run
  • Verify we can get the schema being dumped against a development server
  • Verify we correctly handle introspection not being enabled on a production server

@sjanusz-r7 sjanusz-r7 force-pushed the add-graphql-aux-scanner-module branch from 1a7b77d to 4075e1a Compare May 21, 2025 16:27
@sjanusz-r7 sjanusz-r7 force-pushed the add-graphql-aux-scanner-module branch from 6110908 to 7277210 Compare May 22, 2025 09:19
@sjanusz-r7 sjanusz-r7 force-pushed the add-graphql-aux-scanner-module branch from a514c80 to 9e4d0c9 Compare May 22, 2025 10:10
name
}
types {
...#{vars_map[:type_fragment]}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we create a rex random identifier?

@jheysel-r7 jheysel-r7 added module needs-docs rn-modules release notes for new or majorly enhanced modules labels May 22, 2025
Copy link

Thanks for your pull request! Before this can be merged, we need the following documentation for your module:

@jheysel-r7
Copy link
Contributor

jheysel-r7 commented May 22, 2025

Looks great @sjanusz-r7, a couple minor comments.

Testing

msf6 auxiliary(scanner/http/graphql_introspection_scanner) > run rhost=127.0.0.1 rport=4000
[*] Running module against 127.0.0.1
[*] 127.0.0.1:4000 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/graphql_introspection_scanner) > check rhost=127.0.0.1 rport=4000
[+] 127.0.0.1:4000 - The target is vulnerable. The server has introspection enabled.
msf6 auxiliary(scanner/http/graphql_introspection_scanner) > run rhost=127.0.0.1 rport=4001
[*] Running module against 127.0.0.1
[-] 127.0.0.1:4001 - Server encountered the following error(s) (code: '400'):
  - GraphQL introspection is not allowed by Apollo Server, but the query contained __schema or __type. To enable introspection, pass introspection: true to ApolloServer in production
[*] Auxiliary module execution completed


msf6 auxiliary(scanner/http/graphql_introspection_scanner) > check rhost=127.0.0.1 rport=4001
[*] 127.0.0.1:4001 - The target is not exploitable. The server responded with an error status code and the following error(s) to the introspection request:
  - GraphQL introspection is not allowed by Apollo Server, but the query contained __schema or __type. To enable introspection, pass introspection: true to ApolloServer in production
  msf6 auxiliary(scanner/http/graphql_introspection_scanner) > check rhost=https://api.catalysis-hub.org/graphql? rport=4000
[+] 3.33.161.45:443 - The target is vulnerable. The server has introspection enabled.
[+] 13.248.131.213:443 - The target is vulnerable. The server has introspection enabled.
[+] 15.197.152.254:443 - The target is vulnerable. The server has introspection enabled.
[+] 35.71.150.51:443 - The target is vulnerable. The server has introspection enabled.



msf6 auxiliary(scanner/http/graphql_introspection_scanner) > run  rhost=https://api.catalysis-hub.org/graphql? rport=4000
[*] Running module against 3.33.161.45
[*] 3.33.161.45:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Running module against 13.248.131.213
[*] 13.248.131.213:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Running module against 15.197.152.254
[*] 15.197.152.254:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Running module against 35.71.150.51
[*] 35.71.150.51:443 - Server responded with introspected data. Reporting a vulnerability, and storing it as loot.
[*] Auxiliary module execution completed

Docker Setup

I setup GraphQL in docker just because I prefer using it over npm. If you're interested in the setup for docs purposes I'll include it here although no pressure it's all preference

docker setup

Files in GraphQL directory required to run container:

➜  GraphQL ls -l
total 24
-rw-r--r--  1 jheysel  staff  121 May 22 11:01 Dockerfile
-rw-r--r--  1 jheysel  staff  388 May 22 11:02 index.js
-rw-r--r--  1 jheysel  staff  157 May 22 11:02 package.json

Dockerfile:

FROM node:18

WORKDIR /app

COPY package.json .
COPY index.js .

RUN npm install

EXPOSE 4000

CMD ["node", "index.js"]

index.js:

const { ApolloServer, gql } = require("apollo-server");

const typeDefs = gql`
  type Query {
    hello: String
  }
`;

const resolvers = {
  Query: {
    hello: () => "Hello, world!",
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: true,
  playground: true,
});

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

package.json

{
  "name": "graphql-server",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "apollo-server": "^3.12.0",
    "graphql": "^16.6.0"
  }
}

Run the container with:

docker build -t my-graphql-server .
docker run -p 4000:4000 --name graphql-server -d my-graphql-server

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
module needs-docs rn-modules release notes for new or majorly enhanced modules
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.

3 participants