Maze

First impressions

This challenge had the following description:

maze corparate just started a new trading platform, let's hope its secure because I got all my funds over there
http://45.134.3.200:9000/

The URL provided us a web to hack with a simple login page:

output

The Login Form

Our first attempt as hackers, is to always try common easy username-password combinations, but in this case they didn’t work. We decided to proxy the requests this site was doing to understand its behaviour, everything looked pretty clean. Our third instinctive thing to look at was to check what’s in the robots.txt, this displayed something interesting:

/sup3r_secr37_@p1

We went directly to this endpoint:

http://45.134.3.200:9000/sup3r_secr37_@p1`

Querying GraphQL

output

This given GUI give us the possibility to go further querying our API through Graphql, the problem was that at the time we were doing the challenge we knew shit about it. So what could we do?

The GUI had a Docs section which we could use to learn more about it. If as we did you are wondering what exactly is GraphQL, we can define it as the following:

GraphQL is a data query language developed by Facebook that was released in 2015. GraphQL acts as an alternative to REST APIs. Rest APIs require the client to send multiple requests to different endpoints of the API to query data from the backend database. With graphQL you only need to send one request to query the backend. This is a lot simpler because you don’t have to send multiple requests to the API, a single request can be used to gather all the necessary information.

We also discovered that by default GraphQL does not implement authentication, this is put on the developer to implement. This means by default graphQL allows anyone to query it, any sensitive information will be available to attackers unauthenticated.

We decided to explore the API just using {} for querying with the number 1, as a first attempt to understand its behaviour.

output

The autocomplete function of the GUI suggested some useful queries available to execute. We decided to use allTraders and then press Play, these attributes included some other items to the query. In the end it looked like this:

output

We observed clearly that this was a base64 encoded string, which decoded is TraderObject:1. This gave us the indication that the number 1 in our query could lead to something. Indeed, after using 1 and pressing enter after the id (in node) and pressing 1 again showed some more options.

output

We added all of them to see where the query was pointing to. After a while adding data, our query looked like this:

output

We can observe that this is a username and a password (is not base64). We tried to login using pop_eax and iigvj3xMVuSI9GzXhJJWNeI as password. Didn’t work, so we simply tried XFT as the username, and we were in.

The Product List

This view show us the classic product list that we normally could find in e-commerces or other sites structured alike:

output

If we observe the URL, a Trade endpoint exists. Which automatically gave us the indication that we would be able to try a SQL Injection. The endpoint queried the database using trade?coin=eth%27, so we started to try this attack with a classic ( 27==' ). We received a 500 error from the application, meaning that the database engine tried to run the query unsuccessfully. But we also tried adding --, syntax that serve us to comment all what comes next in the query, and we got the same page result as with trade?coin=eth. So we confirmed that there is a SQL injection vulnerability here.

SQL Injection

We started to map the database testing several times the kind of of output it was giving us each time we modified the query of the attack:

  • /trade?coin=eth%27%20or%20false-- Returned the same result.

  • /trade?coin=eth%27%20or%20true-- Returned XFT coin instead of Eth.

  • /trade?coin=eth%27%20or%20true%20order%20by%201-- Returned BTC and we ordered results by the first column (1).

  • /trade?coin=eth%27%20or%20true%20order%20by%202-- Returned XFT and we ordered results by the second column (2).

  • /trade?coin=eth%27%20or%20true%20order%20by%203-- We ordered results by the third column (3) and returned error 500.

With this last try, we can interpret that there must be only 2 columns in current table.

  • /trade?coin=eth%27%20union%20select%20%271%27,%272%27-- We tried to show constant values like 1 or 2.

output

SQL Injection UNION Attack

Once we knew the basics about our current table, we thought it would be nice to explore other database fields from the same and other tables. We decided to use an UNION attack this time, with the following attributes:

  • /trade?coin=eth%27+UNION+SELECT%20title,body+from+coins-- We observed that there is a title and a body field in the table.

output

We figured out that there is a coins, an admin table and maybe others, but these were interesting enough to play with them first. So we ran a classic:

  • /trade?coin=eth%27+UNION+SELECT%20username,password+from+admin-- And we got a password.

output

For more information about SQLi attacks, we recommend to check the PortSwigger Academy exercises and documentation.

The Admin Panel

We tried to log in as Admin in the admin section with the credentials we retrieved: username admin and password p0To3zTQuvFDzjhO9 we got into another view. At this point we understood that the meaning of the challenge name was no joke.

output

So what can we do with a dialog like this?

When no attacks come up to your mind easily, look at the source.

output

We observed here that there are 2 scripts, a css file and a javascript variable name with value skid. Looking at scripts.js we saw that a global variable name is acceeded and appended to the message that was being animated in the dialog.

output

We decided to intercept the request, and noticed that a cookie was set with a value defined as skid.

output

So we changed the cookie’s name value to fernetInjection to see what happened, sending the request from Burp Proxy to Repeater, changing there the value and pressing send.

output

The string we put in the cookie name’s value is set as the value of the javascript’s variable name.

At this point we decided to try several attacks, but a loved one by our Bug Bounty hunters from the team came up to our minds.

Template Injection Attack

We saw that appending {{}} to the name the server returned a 500 error.

The best way to confirm if there is a template injection vulnerability is to enter some simple math operation like {{4*4}} so when the server render this part of the code, the engine executes 4*4, giving us 16.

Effectively, the engine took the value from the cookie and rendered it without any validation. With the help of this graph we discovered which template engine was being used:

output

And here we attempted the worst attack we could execute through this vulnerability: RCE through template injection.

RCE Through Template Injection

Googling “jinja2 rce" we found a lot of valuable information in the following blog: https://www.onsecurity.io/blog/server-side-template-injection-with-jinja2/

We tried some of the payloads listed and probed that we could indeed execute them successfully. The following example tried to read /etc/passwd in the server:

output

So our flag.txt must be somewhere near. We simply decided to read that file from the same directory as our first approach, and we got:

output

We escaped the maze.

Flag: flag{u_35c@p3d_7h3_m@z3_5ucc3ssfu77y9933}


Post written by @BrOoDkIlLeR & @encodedwitch