In this blog, I will walk through one of the challenges I recently did on HackerOne CTFs, which found very awesome since it involved a lot of vulnerabilities within it.
Investigating the Demo App
First, let’s start with the Demo application of Ticketstatic which allows us to check its functionalities and understand how it works.
We could call this phase of our hacking “the application recon”. In this phase, we only try to see how the app is working, what function it has and map the application as much as we can.
The Admin panel has two main functionalities.
- They can create new users
- They see the tickets sent by other users (including anonymous users who does not have any accounts)
XSS in lists of tickets (on admin area)
One of the vulnerabilities that I always test for especially if I think my payload might somehow be loaded in some other applications like admin applications is the Blind XSS.
In this situation, since we have access to the demo admin panel it’s not needed to test for blind and inject some sort of payload that triggers a notification to us.
Let’s submit a new ticket with a simple XSS payload and then check it in the admin area.
This XSS is very precious since now we can craft a request to force the victim to create our desired user by calling the /newUser endpoint.
CSRF in newUser endpoint
A big note here is that, even if this endpoint was protected by a CSRF token, will still be able to craft such a request since XSS has access to the whole DOM.
We could extract the token from the body response or extract it from localStorage, Cookies (if the httpOnly flag is not set to true)
Adding a new user, we can notice the endpoint is not protected against CSRF attacks.
- No presence of any non-standard headers
- Not using a randomly generated token to make it impossible for the attacker to forge a request.
- The same-site attribute of the authentication cookie was not set to “none” to make it impossible for the attacker to include it in a forged request from other domains.
- The referrer header was not checked at all.
So in order to exploit this we even didn’t need an XSS but anyway let’s follow the way CTF was designed for us.
Forcing admin to create our user with the help of XSS
Now let’s craft that request I earlier said to create our desired user via the XSS we found in the admin area.
There are a lot of ways to make HTTP requests, from a dedicated API like “xhr” to a simple image tag with an src attribute set to the request we want to make (for GET requests)
In order to create a new user, the app used this newUser GET endpoint and a couple of parameters like so:
https://65882848179f197b226b3c9032aca654.ctf.hacker101.com/newUser?username=amir1&password=amir123&password2=amir123
It is worth mentioning that GET requests should not be used for state-changing requests and it’s not safe.
We can now create our user by injecting this simple tag:
<img src="https://65882848179f197b226b3c9032aca654.ctf.hacker101.com/newUser?username=attacker&password=attacker123&password2=attacker123">
But before doing that, we need to go to the main Ticketstatic application.
The other things that might catch your eyes might be to test for SQL-Injection in admin login and also brute-forcing common admin usernames and passwords which eventually failed.
These are good things to test too and you should always test them both in CTFs and real-world but they are not really common in real-world scenarios since sensitive endpoints are all protected against brute force.
Okay, now let’s wait a minute and try to log in with “attacker” “attacker123” credentials.
The first flag could be found in the “Flag Won’t Work”.
SQL Injection via “id” parameter
For error-based SQL Injections always test with payloads like:
' " ‘) “) ‘)) “))
This will raise an SQL error like the one below. This is well enough to verify we have an SQL Injection in this parameter.
We can get headaches exploiting this manually or simply use the SQLMap tool to easily exploit it and obtain the flag :D
I will demonstrate both but highly recommend using SQLMap for those who already have solid SQL knowledge.
Manual Exploit
Figuring out the number of columns
For a successful union-based exploit we need to know the number of columns the query is returning, one of the oldest tricks to do so is to use “order by” clause.
- /ticket?id=1+order+by+1 -> ok
- /ticket?id=1+order+by+2 -> ok
- /ticket?id=1+order+by+3 -> ok
- /ticket?id=1+order+by+4 -> OperationalError: (1054, “Unknown column ‘4’ in ‘order clause’")
This verifies that the number of columns retrieved in the query is 3, so in our union query we should only retrieve 3 columns otherwise the query will be broken.
Using built-in functions for your PoC
In real-world scenarios, we must avoid dumping the data and use other methods as our Proof of Concept.
Using built-in functions of the related DBMS is a great way to do so.
1. Retrieving table names
To retrieve table names I always used limit and group_concat to figure out the number of table names and select them but the nice blog post which I found the other day will teach some cool tips that definitely worth reading. https://infosecwriteups.com/a-technique-that-a-lot-of-sql-injection-beginners-dont-know-atmanand-nagpure-write-up-abdc7c269dd5
With limit we could use:
-1+union+all+select+table_name,null,null+from+information_schema.tables+where+table_schema=database()+limit+0,1--
-1+union+all+select+table_name,null,null+from+information_schema.tables+where+table_schema=database()+limit+1,2--
We could also use group_concat built-in method:
-1+union+all+select+group_concat(table_name),null,null+from+information_schema.tables+where+table_schema=database()--
2. Retrieving column names
The same methods can be applied to column names.
-1+union+all+select+group_concat(column_name),null,null+from+information_schema.columns+where+table_schema=database()--
3. Retrieving the data
Now that we have the table and column names, we can easily select the data
-1+union+select+id,username,password+from+users--
SQLMap
SQLMap is a great tool that can automate the process of exploiting an SQL Injection.
But using it sometimes is a little bit tricky. It is all about adjustment; you have to adjust it in a way that does not produce false-positive results.
This might involve using tampers in case of WAFs block certain characters, using delays, random user agents and etc.
For this challenge, make sure you include your authentication cookies.
The second flag is now obtained using SQLMap.