Solving Apollo 1337 from San Diego CTF 2021
Posted on Mon 10 May 2021 in CTF by 0xm4v3rick
The author note for this CTF challenge.
1 2 3 4 | Apollo 1337
Hey there intern! We have a rocket launch scheduled for noon today and the launch interface is down. You'll need to directly use the API to launch the rocket. No, we don't have any documentation. And quickly, our shareholders are watching!
Website
https://space.sdc.tf/
|
Visiting the website shows below page indicating frontend and backend status for the website.
As mentioned in the challenge note we will need to use the API, so lets look at the requests generated. We see a status API showing information about the health of the services.
Request:
1 2 3 4 5 6 7 8 9 10 | GET /api/status?verbose= HTTP/2
Host: space.sdc.tf
Cookie: __cfduid=d78c68cc9fdea32fb380f38f0ab1825a21620452158
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://space.sdc.tf/
Te: trailers
Connection: close
|
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | HTTP/2 200 OK
Date: Mon, 10 May 2021 04:26:50 GMT
Content-Type: application/json; charset=utf-8
Etag: W/"62-Psu3r0I1RtWvipMsnje4R4c3EqQ"
Vary: Accept-Encoding
Via: 1.1 google
Cf-Cache-Status: DYNAMIC
Cf-Request-Id: 09f6202db000004ae6c5a60000000001
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=WDZCmPIVBqPrVD05F5RqOiX74KykdTJ7WtAttSKScS439NAfEhz34z63qWUzQU0jZCdY3cwvA3nsVH%2BZ8QCtuSYfqsGPJdYhYRB6f9A%3D"}],"group":"cf-nel","max_age":604800}
Nel: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
Cf-Ray: 64d0695c49ad4ae6-HYD
Alt-Svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
{"status":"health","longStatus":"Healthy. All routes are functioning properly.","version":"1.0.0"}
|
Not much information available here. You will notice a get parameter verbose with no value in the request. If you have used sqlmap or such tools you would notice verbosity usually ranges from 1 to n depending on the tool. So lets try providing 1 as value for the verbose parameter.
Request:
1 2 3 4 5 6 7 8 9 10 | GET /api/status?verbose=1 HTTP/2
Host: space.sdc.tf
Cookie: __cfduid=d78c68cc9fdea32fb380f38f0ab1825a21620452158
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://space.sdc.tf/
Te: trailers
Connection: close
|
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | HTTP/2 200 OK
Date: Sat, 08 May 2021 06:08:53 GMT
Content-Type: application/json; charset=utf-8
Etag: W/"e3-jMu7i7AkcsLoz7i0SVBQzLxLr4I"
Vary: Accept-Encoding
Via: 1.1 google
Cf-Cache-Status: DYNAMIC
Cf-Request-Id: 09ec30e10100002e779a2f6000000001
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=MJvYv4dKRoKmfL5Jv%2Fx%2B0QhcW5XFEffkA9JYcDYdVF4bdgi8cPDgqEb5Rng4OxAg4myNma5%2BPgzeK0TnSqtb8XNy%2FhwNaPKwzZZIAds%3D"}]}
Nel: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
Cf-Ray: 64c08414cb452e77-BOM
Alt-Svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
{
"status":"health",
"longStatus":"Healthy. All routes are functioning properly.",
"version":"1.0.0",
"routes":[
{
"path":"/status",
"status":"healthy"
},
{
"path":"/rocketLaunch",
"status":"healthy"
},
{
"path":"/fuel",
"status":"healthy"
}
]
}
|
Now we see a lot more information available to us, which includes 2 new endpoints - /rocketLaunch and /fuel. Lets query the /rocketLaunch endpoint.
Request:
1 2 3 4 5 6 7 8 9 10 | GET /api/rocketLaunch HTTP/2
Host: space.sdc.tf
Cookie: __cfduid=d78c68cc9fdea32fb380f38f0ab1825a21620452158
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://space.sdc.tf/
Te: trailers
Connection: close
|
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | HTTP/2 400 Bad Request
Date: Sat, 08 May 2021 05:39:30 GMT
Content-Length: 25
Etag: "19-SVOfstlPsXCdH4leAIVRqoZbYqg"
Via: 1.1 google
Cf-Cache-Status: DYNAMIC
Cf-Request-Id: 09ec16012700004ada98104000000001
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"max_age":604800,"group":"cf-nel","endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=hI2Bwnxp19DS3Ab9SnIWpCZJ9s1wgOZhERFWg5VMdLFlDLH4R57JOb7MncfLZN3px%2Bb%2BDUX%2FMvK%2FU73NPDhWIt7hls1WGzFSbf%2BDqDI%3D"}]}
Nel: {"max_age":604800,"report_to":"cf-nel"}
Server: cloudflare
Cf-Ray: 64c0591508c54ada-HYD
Alt-Svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
request body must be json
|
We get an error suggesting that the request body must be json. So lets go ahead and do that. From here on the challenge is all about troubleshooting based on the errors we get. We add Content-Type: application/json and a json body to the request as well as making it a POST request since it requires a request body.
Request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | POST /api/rocketLaunch HTTP/2
Host: space.sdc.tf
Cookie: __cfduid=d78c68cc9fdea32fb380f38f0ab1825a21620452158
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://space.sdc.tf/
Te: trailers
Connection: close
Content-Type: application/json
Content-Length: 9
{"x":"y"}
|
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | HTTP/2 400 Bad Request
Date: Sat, 08 May 2021 05:42:51 GMT
Content-Length: 20
Etag: "14-TBPT3N4WkgaLR8mc0RAXcU26ZAM"
Via: 1.1 google
Cf-Cache-Status: DYNAMIC
Cf-Request-Id: 09ec1911d300004aed96084000000001
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"group":"cf-nel","endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=VqP%2FgdJ1wT7IEcEOMkYveux7cy%2F0pihioWDUg%2FO3ifwNRmyxT4gxi8vGEKSTRU8iwExg5mVjVqVOU2yIUwrP%2FSVaUXgVnNx99bDnEHA%3D"}],"max_age":604800}
Nel: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
Cf-Ray: 64c05dfc8c304aed-HYD
Alt-Svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
rocket not specified
|
We get another error asking us to add a rocket. So lets do that.
Request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | POST /api/rocketLaunch HTTP/2
Host: space.sdc.tf
Cookie: __cfduid=d78c68cc9fdea32fb380f38f0ab1825a21620452158
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://space.sdc.tf/
Te: trailers
Connection: close
Content-Type: application/json
Content-Length: 14
{"rocket":"y"}
|
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | HTTP/2 400 Bad Request
Date: Sat, 08 May 2021 05:43:02 GMT
Content-Length: 41
Etag: "29-IJrJmM4FBfsLZi5Mi6TsnwYBLdY"
Via: 1.1 google
Cf-Cache-Status: DYNAMIC
Cf-Request-Id: 09ec193a8700004aed7433d000000001
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"group":"cf-nel","endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=PcLh8HANUGlbAHr1kaVAUzvAv2qT0yCg5VMOY%2BFo39sa%2F7klwj3ss%2FttQBSyCE8%2BrXkWcXf4nvCt4J8OeyHE6ffQCU3k%2BBjSyJgPcMo%3D"}],"max_age":604800}
Nel: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
Cf-Ray: 64c05e3dab4f4aed-HYD
Alt-Svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
rocket not recognized (available: triton)
|
Since the rocket name is not correct, the error provides us with the name which we can use.
Request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | POST /api/rocketLaunch HTTP/2
Host: space.sdc.tf
Cookie: __cfduid=d78c68cc9fdea32fb380f38f0ab1825a21620452158
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://space.sdc.tf/
Te: trailers
Connection: close
Content-Type: application/json
Content-Length: 19
{"rocket":"triton"}
|
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | HTTP/2 400 Bad Request
Date: Sat, 08 May 2021 05:43:11 GMT
Content-Length: 24
Etag: "18-4SZ7qR96LADOnmIcaDt9Ocnl2bE"
Via: 1.1 google
Cf-Cache-Status: DYNAMIC
Cf-Request-Id: 09ec195ebc00004aed5485e000000001
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"group":"cf-nel","endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=jCFFlnfNbKeXYqFkEYm6CQrgxeyYH9sdDK8yk9NRI4eQYF0cgQLJsGVAWP417GFmaWoGSyKdBKgggJZokHLUNZZz2sDwYYAg9WW8KyA%3D"}],"max_age":604800}
Nel: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
Cf-Ray: 64c05e77993c4aed-HYD
Alt-Svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
launchTime not specified
|
Further error wants us to specify the launch time. After resolving few more errors such as launchTime not in hh:mm format and launchTime unapproved I was able to get a proper request going. Launch time is noon based on the challenge description. Any other time would result in unapproved error. We could have also tried the server date/time based on the response to see if it works if 12:00 was not working.
Request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | POST /api/rocketLaunch HTTP/2
Host: space.sdc.tf
Cookie: __cfduid=d78c68cc9fdea32fb380f38f0ab1825a21620452158
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://space.sdc.tf/
Te: trailers
Connection: close
Content-Type: application/json
Content-Length: 40
{"rocket":"triton","launchTime":"12:00"}
|
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | HTTP/2 400 Bad Request
Date: Sat, 08 May 2021 05:45:43 GMT
Content-Length: 25
Etag: "19-mztLDY8Mq3B5NlRcCCz93/AW9/E"
Via: 1.1 google
Cf-Cache-Status: DYNAMIC
Cf-Request-Id: 09ec1bb20200004b0a94a2f000000001
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=vNRmX6pCeEMNbFdJFpBoha38myORB9sQFmLzXc%2FZpdhCQdYvYemzzkWTD2bYyZs3XCHAbShDYco72bq65PpQnXmHMzmKrPA79pUBmMk%3D"}],"max_age":604800,"group":"cf-nel"}
Nel: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
Cf-Ray: 64c0623009ce4b0a-HYD
Alt-Svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
fuel pumpID not specified
|
We now have to add pumpID value. We can get it by querying the /fuel endpoint or bruteforce the numerical value in this request. After trying available pumpIDs we get the one that works.
Request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | POST /api/rocketLaunch HTTP/2
Host: space.sdc.tf
Cookie: __cfduid=d78c68cc9fdea32fb380f38f0ab1825a21620452158
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://space.sdc.tf/
Te: trailers
Connection: close
Content-Type: application/json
Content-Length: 51
{"rocket":"triton","launchTime":"12:00","pumpID":4}
|
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | HTTP/2 400 Bad Request
Date: Sat, 08 May 2021 05:47:00 GMT
Content-Length: 42
Etag: "2a-v5ExyC0zETE7PzLwSkFsUIYG9H0"
Via: 1.1 google
Cf-Cache-Status: DYNAMIC
Cf-Request-Id: 09ec1cddd000004af8321a7000000001
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=WYVsqRAJnviX%2FFIDgpJ0rK2EM9%2B%2Fjx6XFpN80Xc91zn6gWjEoL2y3sDIph3zLWiLPuMO0fy3NOs4xW0LUrflB%2BtyVL13ISK1N0cJ4Eg%3D"}]}
Nel: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
Cf-Ray: 64c0640fbda44af8-HYD
Alt-Svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
frontend authorization token not specified
|
Its now asking for a authorization token. So I went ahead and searched for token in the burp history. All the js files had a token as below
1 | Token:"yiLYDykacWp9sgPMluQeKkANeRFXyU3ZuxBrj2BQ"
|
This is where I was stuck a bit and started thinking too much. I am used to dealing with auth tokens in request headers, hence I tried to add this token as a header. I tried various auth related headers and a few combinations of them but nothing worked. Than I took a step back and tried to approach it in a different way and thought of trying to add token as another json value with others and that worked resulting in below request and the flag.
Request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | POST /api/rocketLaunch HTTP/2
Host: space.sdc.tf
Cookie: __cfduid=d78c68cc9fdea32fb380f38f0ab1825a21620452158
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://space.sdc.tf/
Te: trailers
Connection: close
Content-Type: application/json
Content-Length: 106
{"rocket":"triton","launchTime":"12:00","pumpID":4,"token":"yiLYDykacWp9sgPMluQeKkANeRFXyU3ZuxBrj2BQ"}
|
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | HTTP/2 200 OK
Date: Sat, 08 May 2021 06:45:30 GMT
Content-Length: 50
Etag: "32-UMBTLpu/GF1MEJ81AK5sO97tnRU"
Via: 1.1 google
Cf-Cache-Status: DYNAMIC
Cf-Request-Id: 09ec526b3d0000d5932c28a000000001
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=Jf%2FOq5YiSaNHAG2nlYIxuXFPaaVftdWL0hK0QPfslFb98eji0%2BTuaT1S0%2FY2688hypXl6dZqHRMOUoWxuY4Fh%2B%2BWzAVQjxS1%2FmiTLXA%3D"}]}
Nel: {"report_to":"cf-nel","max_age":604800}
Server: cloudflare
Cf-Ray: 64c0b9bec975d593-BOM
Alt-Svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
rocket launched. sdctf{0ne_sM@lL_sT3p_f0R_h@ck3r$}
|
This was how I approached the problem and solved it. Feel free to ping me on twitter for feedback or queries.
- 0xm4v3rick