HTB UNIV ctf 24 (Armaxix - WEB)
In this writeup, we’ll explore a lab instead of focusing on a new bug in bug bounty. While CTF challenges teach us a lot, they often differ from real-world scenarios. However, it’s crucial to extract the important lessons they offer.
In this lab, we chained two vulnerabilities to achieve command injection. The setup includes access to the source code and two distinct hosts.
83.136.254.158:45660 → This is Armaxis. I’m not entirely sure about its main purpose, but it includes functionalities for sending items to users via the admin, resembling a report-sending system.
83.136.254.158:33602 → This appears to be a mail service, similar to Gmail.
Before examining the source code, I tested everything I could think of: JWT manipulation, authentication bypass, and SQL injection. However, none of these approaches worked.
I then focused on the password reset functionality, where I discovered an account takeover vulnerability through manipulation of the password reset request.
Below is the password reset request body:
{
"token": "f35137e53d9d900a8435a9734a79165d",
"newPassword": "f35137e53d9d900a8435a9734a79165d",
"email": "test@email.htb"
}
The request body is in JSON format. To achieve account takeover (ATO), we simply need a valid token. This can be obtained by requesting a password reset for the target account test@email.htb
via the reset password page. The token will then be sent to the mail service host.
After that, I’ll receive the token.
After digging further into the source code, I found the email admin@armaxis.htb
.
Now, I’ll obtain a valid token, manipulate the request, and replace the test email with the admin email.
This is how the request body looks after manipulation:
Boom! We’re now the admin, and we have access to some interesting functions to explore and test.
dispatch weapon page contains a form.
The “note (MarkDown)” section is the most interesting one. I then returned to the source code and found a file named markdown.js
, which contains the Markdown handler. As part of it, there is a URL handler that executes a curl
request when a URL is provided in the Markdown.
const fileContent = execSync(`curl -s ${url}`);
To achieve command injection, we simply need to separate the commands by adding a semicolon at the end of our URL.
Of course, I initially tried using a normal URL (http://...
), but it didn’t work. So, I looked into how URLs are handled in Markdown, where there are two formats: []()
for regular URLs and ![]()
for embedded images.
In my first attempts, I focused only on []()
, which was a mistake. It’s better to test using a wordlist that includes all URL-related functions in Markdown. After getting stuck with []()
, I returned to the source code and found the following:
return `<img src="data:image/*;base64,${base64Content}" alt="Embedded Image">`;
This means the code expects an embedded image URL rather than a regular one. So, I tried ![](file///etc/passwd)
, and it worked! I was able to retrieve the content of /etc/passwd
encoded in base64.
Now that I can execute any command by separating it with a semicolon (;
), I decided to focus on retrieving the flag. First, I used pwd
to check my current directory. Then, I used the following command:
`![](file://;cat flag_path.txt)
`
This allowed me to retrieve the flag content as base64. I simply decoded it to get the flag.
HTB{FAKE_FLAG_FOR_WRITEUP}
You just have to try everything you can. For each function, treat it like an application focus on it until you’ve explored all possibilities (reading docs, writeups, reports, and tweets…) to gather as many tricks and tips as possible. When you encounter a scenario (e.g., a file upload that only accepts SVG files), try techniques like XXE.
I hope this article sheds light on some of the darker areas in your understanding. Next time, Inshallah, we’ll tackle a critical bug!