(English) Cybersecurity Students Writeup 2025
In this cybersecurity student competition, our team made it to the finals of Group B, and below are the web challenges we solved, mainly web challenge sections.
Challenge Leak Force

First, in the challenge, we’ll see a login page. Let’s create an account and log in. The HTTP history looks like this:
As we can see, when logging in, it fetches a random /api/profile?id=1492 and returns a response with the user’s information:
1 | {"id":1492,"fullName":"solve 123","username":"anhphuc","email":"anhphuc@example.com","description":"New user","avatar":"https://i.pinimg.com/1200x/d3/88/5e/d3885e4a5748dddbb9b874dc0cf6fabd.jpg","birthdate":null,"gender":null,"company":null} |
After reviewing it, I discovered that this web application has an IDOR vulnerability that allows viewing other people’s account information by changing the ID parameter:
And the source code shows that we have an admin page user. After logging in, the flag will be displayed as follows:
1 |
|
Okay, so we can identify the vulnerability now. How can we get the Admin user? Luckily, this application has a user password update function. We can update the current user’s password.
535
Ah, I found the admin ID is 1. So now, let’s assume we can exploit the password update to update the admin ID. Yes, we can update it. Let’s see:
Now we can log in with the admin user and proceed to get the token.
Then, proceed to retrieve the flag by sending the admin token and successfully get the FLAG.
This part took me quite a long time even though it’s easy because many people accessing the server might update the admin password repeatedly, making authentication impossible.
Challenge ZC-1

Let’s analyze the first challenge. When I accessed the website, I saw that it returned a 404. I thought the challenge was faulty, but when I asked the author, they said that’s how it is :v
There’s not much to do here, let’s delve into the source code to analyze in detail why.
Source Code: ZC-1
The source code has two parts: app1 and app2. App1 uses (Django + DRF, port 8000) and app2 uses (PHP Apache, port not published), which is why the application returns a 404 not found error. After examining the structure of app1, it has routes /gateway/user/ used to create new users.
1 | from django.urls import path, include |
With the corresponding username, password, and email parameters:
1 | from rest_framework import serializers |
Go to /auth/token/ route to get the token or recreate the token of the newly created user
1 | urlpatterns = [ |
Then we can pass data via the route /gateway/transport to receive a compressed file from the user
1 |
|
Then call the checkfile function to check the allowed file type
1 | STORAGE_URL = env("STORAGE_URL",default="http://127.0.0.1:8002") |
1 |
|
then call transport_file() to push the file straight to app2/src/storage.php
1 | def transport_file(id, file): |
Next, it will call /gateway/health?module=… call requests.get(STORAGE_URL + module) and only return “OK/ERR” according to the HTTP status, not the body.
1 | def health_check(module): |
And in app2, in the file storage.php, use the gemorroj/archive7z library to wrap 7-Zip to extract the correct file we uploaded to /var/html/storage/
1 |
|
With the detailed analysis above and after further research, I discovered a vulnerability in 7z where symlinks can be used to arbitrarily overwrite files during extraction, causing 7z and we can build a POC like this page POC
Exploitation.
Now let’s create a user to get the corresponding token and UID

Proceed to call /auth/token to authenticate the token with the username and password of the newly created user.
Get user_id
Next, we have the exploitation process
- First, we create a valid file in app1 to check and compress it into a zip file
- Then create a revershell file Revershell PHP using 7z to zip it
- Then put the two files into one arbitrary file
- Proceed to upload the shell
- Then call gateway/health/?module=/storage/$USER_ID/shell.php to activate our uploaded shell
Below, I will summarize the above processes step by step as follows:


Now the server revershell has successfully received the shell from our trigger.
Then, interacting with the command, we get the FLAG:
CSCV2025{Z1p_z1P_21p_Ca7_c47_c@t__} Unfortunately, I solved this problem at the last minute and couldn’t submit it :v
Challenge PortfolioS

First, when you enter the challenge, it will display the login and registration page. Let’s try creating a new user. After logging in, it redirects to the main page, and the HTTP history is as follows:
After entering any value, it will save it as portfolio_
And the downloaded value has the format of an md file as follows:
We can’t do much here, let’s go into analyzing the Source Code First, we will find where the flag is located. I found it in the Dockerfile. The random value has 16 characters, and the application runs in Java. This challenge hints from the author that it’s written in SpringBoot.
1 | FROM openjdk:17-jdk-slim |
In the nginx route we can see it will block us to /internal/testConnection
1 | events {} |
After researching, I found that the server uses nginx version: nginx/1.20.2
After researching, I found the document Exploiting HTTP Parsers Inconsistencies which can bypass it in Spring Boot by using the character \x09. Here, Spring Boot will remove the character, but on the nginx server, it doesn’t allow us to bypass it. Let’s do it as follows:
Then, enter the random username and password parameters, and it will report an error: yes, we can see the source code in this internal route.

In the source code, we can see the application This method uses the username and password directly appended to the parameters without input filtering, allowing an attacker to insert commands to RCE.
1 |
|
Also, it uses a common error message, which can leak information through its error message.
1 |
|
After reviewing and investigating, I learned that this application has a vulnerability: H2 JDBC Connection String Injection → RCE. However, when I got to this point, I tried to find a way to call ALIAS EXEC to execute or even write to the file, but I still couldn’t get the application to trigger RCE [……]




