Reflections & Learnings
The reflections of the challenges I have completed in one place. They are ordered in the order that I completed them.
I have found that writing reflections on the challenges I have completed has been very beneficial in helping me understand the concepts and techniques that I have learned. It has also helped me to remember what I should do in future challenges, and can serve as a good resource for people who are looking to learn more about web pen testing as well.
The use of eval()
can pose a lot of security risk (as evident) in this challenge, exacerbated by the fact that the user input was unfiltered.
Our primary takeaway here is that eval()
should really just not be used whenever possible due to the possibility of RCE and DOM Based XSS vulnerabilities. While we didn't utilise an XSS based attack in this challenge (since it wasn't needed), there was no reason why we couldn't do some other nefarious acts.
Instead, we should potentially look into using other, safer alternatives, such as JSON.parse()
for parsing JSON, or even just utilising robust, popular libraries that handle expression evaluation securely.
Whenever a website opens its doors for users to input their own data - this could be a potential risk. It is paramount that any user input is properly validated and sanitised thoroughly, to ensure that we are only parsing and processing data that we know is safe (as much as possible).
Even if there was a need to use eval()
(which there really shouldn't be), the input should at least be filtered.
This first challenge has made me realise that there are so many things to think about when working with software in a security context. Before taking this course, I had some trivial knowledge of what not to do when developing software (e.g. storing passwords in plain text, using env
variables when possible etc.), but this first challenge alongside all the CTFs have made me realise the reality.
This is why it is so important that during code development in teams, we have comprehensive code reviews and testing. Sometimes, just having an extra pair of eyes could be the difference between a safe website and a vulnerable website (like this one), as others can offer another perspective over your code that you may not have. Beyond this, security and penetration testing could be another way to help mitigate vulnerabilities overall.
It was pretty clear that the main vulnerability of this challenge was the misconfiguration in the setup of the server, which inadvertently allowed for the external exposure of sensitive directories, like /storage
.
In this case here, they were using the folder to allow for people to see the raw configuration files that they are generating. At the very least, they should have placed the generated files into a different folder, and used a network configuration that would lock down the access to that specific folder so such file traversal attack swouldn't be possible.
It's important to note that the database backup file was stored in the/storage
directory, which was exposed to the public. This is a huge security risk, as it clearly was the downfall of this particular website. While the misconfigured directory access was the main issue, the fact that this database backup was also stored within the same folder was also an oversight in itself.
Backups like these should be encrypted and stored in a secure location, and not in a directory that is accessible to the public.
The password for the admin account was adminadmin1
. Clearly, this was a weak password, and this particular challenge exposed why we should avoid using weak passwords in the first place. There are plenty of websites out there that provide a rainbow table of common passwords to their leaked hashes, and it's really not difficult to find the corresponding password to a hash.
While the developer's minds were in the right place in regards to storing the passwords as a hash, they should have also enforced a password policy that would have prevented the use of such a weak password. In addition, the MD5 hash that was used was also a weak hashing algorithm, and should have been replaced with a more secure hashing algorithm like bcrypt, which includes a salt to prevent brute-force attacks.
Since this challenge didn't provide the source code, it was difficult to determine what we were looking for, and what would we be useful. In my case, I spent too long trying to exploit the SQLi vulnerability that I thought was there, when in fact, it was a red herring (I'm not entirely sure whether or not there was a SQLi vulnerability in the end).
This could be a problem if I was actually doing real web penetration testing, as the time that I wasted on the SQLi could have been better spent on looking for the actual vulnerability, and it would be giving the developers and maintainers more time to identify that I was trying to exploit their website. From here, I started thinking about giving myself some time limits on certain tasks, and if I can't find anything within that time, then I should move on to something else, but keep it in the back of my mind.
From this challenge, I realised the usefulness of using existing tools, like using the rainbow table website to crack the password hash, instead of relying on brute-force methods and writing my own script for it. This can help save a lot of time and effort.
This was another example of why it is paramount to filter your inputs. All of this attack could have been avoided if the developer had filtered the input that was being passed, instead of directly unserialising it. This is a classic example of a PHP Object Injection vulnerability.
A developer (in the real world) should check for any unused code within the codebase, as this could potentially be a security risk. In some cases, it could be possible that code that has been left in the codebase contains legacy, vulnerable code.
This challenge was a good example of why it is important to use contextual clues to help one solve a challenge. In this case, the name of the challenge, "Pop Restaurant", was a hint to the fact that we would need to use a POP chain attack to get the flag. Knowing this, I now know in the future to potentially use such clues to help me solve a challenge - particularly when I'm stuck (like I was with this challenge in the middle).
This challenge was a good example of how seemingly innocuous code can have a big impact. In this case, the magic methods that were defined in the models were actually the entrypoints for the attack. This is a good reminder to always be vigilant when looking at code, and to not dismiss anything as 'not important'.
If I were to write a website in PHP, I could imagine that these magic methods are actually very useful when used correctly. However, in this case, they were used against the application.
This challenge was a good reminder to me that complacency is quite a problem - my initial thoughts thinking that the challenge would be easy due to the familiarity of the language was a mistake. The challenge - while not particularly difficult - had a lot of red herrings that complicated the process of identifying the core vulnerability.
This underlined a lesson - familiarity with a language is good, but doesn't translate to ease, especially in a security context. Luckily, from previous challenges, I had already learnt to not dwell too long on a particular idea if I wasn't getting too far with it - which helped me to not get too bogged down in the red herrings.
A key change in the way I approached this challenge I think was the fact that I took a bit more time to note down the things that I found interesting, before I went in to start planning an approach - you may notice a specific reconnaissance section in the more recent writeups. This helped me scope out the challenge, get a feel for what I was working with, before jumping in.
Not only did this prove very useful in not getting too far down any particular rabbit holes, it also helped me to keep track of what I had already tried, and what I hadn't. In addition to this, keeping track of some of the things I noticed (e.g. the FetchServerInfo
function) helped me to quickly make the connection once I found the right vulnerability, despite it first seeming to be a red herring.
I do also realise the importance of not diving in too quickly in real web penetration testing - starting immediately with no plan could lead to wasted time, and giving time for the target to potentially detect you.
This challenge was my first encounter with Server-Side Template Injection (SSTI), and it was quite an interesting experience. When used correctly, SSTI is a powerful tool for generating dynamic content, but when used incorrectly, it can lead to a lot of pain.
This once again underlines the importance of input validation and sanitisation - the vulnerability was present because the application allowed for the rendering of any template file given by the user.
Of course, we hear all the time that we should keep our tech stack up to date to avoid accumulation of tech debt leading to vulnerabilities.
I certainly hope and think that I'm not the only one who thought that this translated to modern languages being fully secure. This challenge was a good reminder that even modern languages can have vulnerabilities, and that it's important to keep up to date with the latest security practices.
Even with a modern language, the biggest vulnerability will always boil down to the person writing the code.
This challenge serves as a good reminder of the importance of testing and code review. The vulnerability in this challenge was a simple logic error, but it was one that could have been easily caught with some testing.
This challenge was a good reminder of the importance of staying persistent but pragmatic. I think it was a good approach to move on from testing for SQLi and JWT bypasses when I realised that these were probably too complicated for the scope of this challenge. Balancing persistence with pragmatism is key in CTFs.
I think it's important to remember that not all CVEs are relevant to the challenge at hand. It's easy to get sidetracked by vulnerabilities that are not present in the version of the software you are working with. It's important to stay focused on the challenge and not get lost in the weeds of irrelevant CVEs.
This challenge was a good reminder that SQLi is still a thing, and that it can still be a very powerful attack vector. In this case, the SQLi was used to bypass the login page, and gain access to the admin dashboard. Sanitise your inputs people!
This challenge was also a good reminder of why 2FA is so important. Even though we were able to bypass the 2FA, it was still a good reminder of why 2FA is so important. In this case, the 2FA was a simple 4 digit code, which meant that we could easily brute force it. In the real world, 2FA would be more secure, as it would be time-based and would (typically) be a longer code.
In a different context, this also shows why passwords and hashes should be long - the longer the password, the less susceptible they are to brute force attacks, as the bits of work required become exponentially larger.
X-Forwarded-For
can be a dangerous header when not handled correctly, since it is so easily spoofed. In this particular case, it was probably safest to rate limit based on the IP address of the client, rather than the IP address in the header.
To add an extra layer of security, the application could utilise user authentication and/or API keys to reduce the reliance on the IP address of the client for rate-limiting.
Knowledge of the frameworks that you are using can be very beneficial in a CTF scenario. In this case, knowledge of Flask and uWSGI was beneficial in understanding how the application was working, and how the different components were interacting with each other.
Don't assume that just because an application isn't exposed to the user, that it isn't vulnerable. In this case, the Python Flask application was hidden behind a basic front-end, but was still vulnerable to SSTI. This is a good reminder that you should always check the entire codebase for vulnerabilities, not just the parts that are exposed to the user.
This also highlights the importance of not running unnecessary services, as the Python Flask application was running on the same server as the front-end unnecessarily.
There is no point in having validation if it can be easily bypassed. Granted in this case, it wasn't 'easy' per se, but it could still be done with the right knowledge. Ideally, we should use a library that has been tried and tested, and is known to be secure.
In comparison to my earlier challenges, I think I figured out the vulnerability in this particular challenge a lot quicker than I would have previously. This was due to the fact that I had a better understanding of how I should go about undertaking these challenges, taking the time to do my recon, plan and research before I even begin executing anything.
Granted, the main challenge in this particular challenge was figuring out the SSTI payload - but that would also come with practice.
The NextJS vulnerability that I exploited was only present in versions up to 14.1.0. The vulnerability was patched in 14.1.1, so keeping your tech stacks up to date is crucial in ensuring that you are not vulnerable to known exploits.
This is especially important in the case of legacy applications, where the tech stack may not be updated as frequently as newer applications. This is a prevasive issue in the industry - and is why security is becoming such a focus today.
Overall Learnings
- I have learned a lot from doing these challenges - from the many different types of web vulnerabilities that exist, how they can be exploited, and the tools and techniques that are used in web pen testing.
- In the beginning, I found it difficult to know what exactly I was looking for and was to do. I didn't have a clear methodology or process to follow. However, as I did more challenges, I started taking the time to perform proper recon, plan, research before executing the attacks. This helped me follow a more clear-cut path, and allowed me to more efficiently and effectively find and exploit vulnerabilities.
- Another key learning was to not spend too much time on a single task when trying to solve a challenge. In my earlier challenges, I would often find myself going deep into a rabbit hole, thinking that I was on the right track, only to find out that I was completely wrong. I learned that it is important to take a step back, and try to look at the problem from a different perspective, using context clues and hints that are given in the challenge. The best example of this was with Pop Restaurant, where I was stuck for a very long time, until I realised that the name of the challenge itself gave a big hint.
- Using the right tools is also very important, as well as knowing when to use tools that already exist. In the beginning, I was doing a lot of manual work, realising that there were tools that already did the job for me, saving lots of time and effort.
- Finally, I learnt about the importance of security as a whole. Before undertaking these challenges, I had no idea how many different vulnerabilities exist out there, and just scrolling through HackTricks and other blogging websites made me realise the scope of this field. It's crazy to think that with humans inventing computing and on the bleeding edge of technology, there are also people who have nefarious intents. It's a constant battle between the good and the bad, with the bad always trying to find new ways to exploit systems and the good always trying to find new ways to protect them.
Process
This is an overview of the process I eventually followed, including tips and resources that I found useful.
The first step in doing these kinds of challenges is to gather intel, or the fancy term being reconnaissance. This involves finding out as much information about the target as possible, such as the technologies being used, skimming through the source code (if provided), playing around with the website normally etc.
What I like to normally look for are:
- Source Code: This is a goldmine of information. I like to look for comments, hidden fields, endpoints, etc.
- Technologies: Knowing the technologies being used can help in finding vulnerabilities that are specific to that technology. These can be found in the
package.json
,requirements.txt
, or even theDockerfile
. - Endpoints: Looking for hidden endpoints, APIs, etc.
- Where the flag is: This is of course your end goal, so knowing where the flag is can help you plan your attack. Sometimes it's in a config, a variable, most of the time it's in a file. There have been some instances where the flag was copied to a random location (as defined in the Dockerfile), so in these cases RCE of some sort is typically involved.
- Obvious vulnerability entry points: Sometimes, vulnerabilities can be spotted very quickly - and may have a part to play in the challenge. Keep a mental note (or physical note!) of it.
Sometimes, if you have little idea of what to do by this point, it may be worth starting to consider vulnerabilities being present within libraries. For example, if you see a NextJS website or HAProxy configuration - look for CVEs on Google - this can often lead straight to some form of answer.
Other useful resources in discovering/finding out vulnerabilities:
- HackTricks - A great resource for finding out about different vulnerabilities and how to exploit them.
- PortSwigger - An online learning platform with tutorials and labs on web security for various pen-testing techniques.
- CVE Details - A database of known vulnerabilities and their details.
- Google! - Google is your best friend. If you're stuck, Google the error message or the technology you're working with.
This is also the stage to research anything that you are not sure of that you are seeing - without pre-requisite knowledge, it can be very difficult to solve some challenges.
The planning stage is where you take all the information you have gathered from the reconnaissance stage and start to plan your attack. This is where you can decide where you should start the attack, then how all the potential individual components could link together. More difficult challenges will almost always involve several steps.
Consider:
- What vulnerabilities you have found: Think about the vulnerabilities we have gathered. How could they be exploited and through what means? Also consider that sometimes one vulnerability may have a direct segue to another vulnerability that you may have missed.
- What tools you are going to use: There are many tools out there for web pen testing - make sure you use them where possible! Software like Burp Suite, OWASP ZAP, and Nmap can be very useful in running along with the vulnerabilities.
- What your end goal is: This is very important! Always keep in the back of your mind what you are trying to achieve. In these web challenges, they are always to gather a flag - but where is the flag? Do we need to get RCE? Read a file using code? Get admin access?
It's go time! In the case of Hack the Box challenges, more often than not they will provide you with the source code of the challenge. I highly recommend you download this and run a local instance of the challenge to test your exploitation plan first, as you have the freedom to do whatever you want, debug, etc.
In real pen testing, obviously it is not possible to run a local instance. In this case, you will have to be very careful with your attacks, as you could potentially take down the website or cause other issues, which could expose you to legal issues. This is why it is paramount to perform the previous two steps first whenever possible to the greatest extent.
And that's it! You've completed the challenge. If you haven't yet, make sure to write a reflection on the challenge you have completed. This will help you remember what you have learned, and can also be a great resource for others who are looking to learn more about web pen testing.