CTF: DC-7, DC-8, DC-9 (hard)

These are the last machines in the DC series — and definitely the toughest ones. Each pushed me harder than the earlier boxes and forced me to level up in enumeration, shell access, and privilege escalation. But they also gave me a big confidence boost: these challenges are starting to feel more natural.

DC-7

Enumeration

We start off with a typical full-port aggressive scan:

So we’ve got a web server running Drupal 8 — time to dive in.

HTTP (80)

At first glance, the website just shows a simple welcome page. Banner grabbing confirmed it’s a Drupal 8 instance. I ran a few quick checks:

  • Manual browsing → led nowhere fast.
  • droopescan → didn’t work due to a dependency conflict (I wasted way too much time on this).
  • gobuster → didn’t turn up anything useful either.

At this point, I felt stuck — so I went back to the basics and started inspecting the source code. That’s when I noticed a small hidden message in the site footer:
@dc7user A quick Google search led me to a GitHub profile — and inside one of the repos, I found a database password:

With no login panel on the site, I took a chance and tried it as the SSH password…

Initial Access

Once logged in via SSH as dc7user, I was greeted with the message: ‘You have mail’ I checked the mail inside /home/dc7user and found a message from root. It mentioned something about a backup script.

Naturally, I went digging and found the referenced script sitting in a directory. The backup script contained a call to a drush command — a CLI tool used for managing Drupal sites. I was allowed to run drush as dc7user. Running drush --help revealed a function to functionality to reset user passwords.

Assuming there’s a default admin user (as is common with many CMS setups), I used drush to reset the password:

Now that I had full admin access to the Drupal backend, I looked for a way to get code execution. There wasn’t any built-in option to add PHP directly to a content page, so I installed a module that allowed execution of PHP in page content:

After enabling that module, I created a new page and embedded a simple PHP web shell.

Escalating Privileges

Earlier, we discovered that the www-data user has permissions to execute the backup script. Now that we have access as www-data through the Drupal web shell, we can take full advantage of that. I edited the backup script and inserted a simple bash reverse shell, pointing back to my attack box.

After triggering the script (or waiting for it to be executed — depending on how the cron or automation is configured), I got a reverse shell back and recieved the flag:

DC-8

Nmap scan:

HTTP (80)

When browsing to the IP, I was greeted with another Drupal site. I started by checking the robots.txt file, which had some common disallowed paths, but nothing too exciting. However, I noticed a bunch of database-related files referenced, so I kept my eyes open for SQL-related bugs. On the homepage, I saw several headers leading to internal pages. Clicking one of the hyperlinks led me to a URL that looked like:

I added a ' to the nid parameter:

I was greeted with a MariaDB error, which strongly hinted at a possible SQL injection vulnerability. I wanted to practice manual SQLi, so I didn’t rush into using sqlmap right away. Basic union testing:

Only one column seemed to be reflected back. When I tried to call database() or other functions, I didn’t get any visible output on the page. No output in the response either → possibly blind SQLi. After a bit of experimenting (and some help from AI ), I used the following payload:

This forced an error message that revealed the database name:

Using similar techniques, I continued with:

This was a slow process and the output was limited (usually one item at a time), so eventually I gave in and used sqlmap to speed things up.

The database had 88 tables, but I focused on the users table. Dumping the data:

Initial Access

After dumping the users and their hashes from the Drupal database, I cracked them using John the Ripper. The password for user john turned out to be:

There wasn’t any visible login link on the homepage, but I remembered to check the robots.txt file — and sure enough, there was a hidden path listed: /user/login Using the credentials john:turtle, I was able to log in. At first, the account didn’t seem very useful — I only had permission to add content, and any PHP I tried to inject was just rendered as plain HTML (filtered out). I did a bit of Googling and clicked around in the admin panel. That’s when I noticed something interesting: the Webform module allowed code injection!

I crafted a PHP reverse shell using revshells.com and pasted it into the Webform field. After some trial and error, it finally connected back to my listener:

Pivilege Escalation

Once inside, I did some basic recon and found two users on the system:

  • root
  • dc8user

Naturally, I focused on escalating to dc8user or root. But when I tried using su or ssh, I got hit with something new:

A Google verification code was required.

That threw me off and sent me down a rabbit hole for a good two hours. 😅 Eventually, I reminded myself to go back to basics. Running a standard SUID check:

I noticed exim4 in the list — an email transport agent. A quick search on Exploit-DB turned up this vulnerability:

🔥 Exim 4.87 – 4.91 – Local Privilege Escalation

I had trouble getting the exploit to work at first, but after watching a short video walkthrough, I got the hang of it and managed to pop root.

DC-9

Nmap scan:

HTTP (80)

At first glance, the webserver didn’t reveal much. There was a Home, Employees, Search, and Login page — pretty standard stuff. I spent the first ~30 minutes poking around for common low-hanging fruit:

  • Path traversal
  • SQL Injection
  • XSS

…but none of them seemed to work on the exposed pages. I briefly considered bruteforcing credentials, but every attempt returned the exact same error — even with gibberish input:

So that felt like a dead end. Running a basic gobuster scan using the default big.txt list:

but it didn’t uncover anything useful:

Then I realized all the visible pages had .php extensions — so I re-ran gobuster with -x .php,.html and a better wordlist:

This gave me much better results:

The most interesting one was /welcome.php, which surprisingly redirected me into the application — without needing to log in:

Even better — the page had a message at the bottom: “File does not exist” This gave me path traversal vibes, and sure enough — I was right:

Turns out, all the staff had accounts with terminal-style logins, but direct SSH access was blocked. I figured there had to be a workaround. This box reminded me how easy it is to miss subtle things:

  • Eventually, I had to peek at a write-up (still learning!) and discovered that unlocking SSH access via port knocking was key.
  • I totally missed a SQL Injection vulnerability on the search page — something I’ll need to practice more manually.
  • I also had no idea what port knocking was — and even AI didn’t help much there.

SQL Injection

I started testing basic SQL payloads and found that the following worked:

That confirmed the injection point and showed there were 6 columns.

Then I started poking deeper:

At this point, I could have used sqlmap to automate everything — but I didn’t want to take the shortcut. In OSCP-style testing, you’re not allowed to use sqlmap, and besides, I wanted to understand the mechanics. So I followed a write-up to learn how to do manual enumeration and dumped a list of usernames and passwords from the database.

SSH Access

Now that I had login credentials, I needed a way to access SSH — which wasn’t open by default. Earlier in the write-up, I learned about port knocking, and used the following sequence to open port 22:

After that, I used Hydra to test the credentials from the dumped DB:

Privilege escalation

After logging in as each user, I checked the usual stuff:

  • sudo -l
  • any sensitive files
  • scheduled jobs
  • SUID/SGID binaries

Nothing interesting… until janitor. In their home folder, I found a curious-looking file:

Looks like another list of credentials. I added them to a new password list and reran Hydra:

Boom — valid login for fredf. Once logged in as fredf, I ran sudo -l:

This binary (test) required two arguments: read and append. After testing it, I figured out that it reads one file and appends it to another. So I decided to append a custom root user to /etc/passwd:

And just like that… 🦇

Lessons Learned

These three boxes were packed with useful challenges — not necessarily the hardest technically, but incredibly valuable in building confidence with realistic enumeration, privilege escalation, and manual exploitation. Here are some key takeaways I’ll carry forward:

Enumeration Is Everything

  • Every machine started with hints hiding in plain sight: hidden directories, services behind knock sequences, or local scripts writable by www-data.
  • Recursive dirbusting and checking HTTP responses manually still pays off.
  • I kept reminding myself: Don’t jump to exploitation too fast. Fully map the surface first.

Manual SQLi Matters

  • I avoided sqlmap on purpose — forcing myself to manually enumerate columns, databases, tables, and users via SQL injection made me actually understand what’s happening under the hood.
  • OSCP doesn’t allow automation tools like sqlmap anyway, so this kind of practice is golden.

Time for My Own Pentest Checklist

  • Sometimes I still miss basic stuff because I get tunnel vision or excited about a new lead.
  • That’s why I’m planning to create a personal checklist for each stage of a pentest: initial enumeration, content discovery, privilege escalation, and reporting.
  • Not only will it help me stay structured during live tests (especially under pressure), but it’ll also make my workflow more repeatable and easier to improve over time.

Creative PrivEsc

  • DC-7 used a writable backup script to trigger a reverse shell.
  • DC-8 required understanding a cron-based script injection path.
  • DC-9 used a custom sudo-enabled binary to write to /etc/passwd. That forced me to think like a dev and break the intended use of a tool.

Each one was a mini puzzle with a different flavor — and I’m definitely building a mental library of escalation tricks from this.

Final Thoughts

This trio (DC-7, 8, and 9) helped cement a mindset I want to carry into real-world pentests:

  • Be patient and methodical.
  • Use automation only when needed — understand what it does first.
  • Focus on misconfigurations and logic flaws, not just exploits.
  • Standardize your workflow to reduce mistakes and build consistency.

I feel a lot more confident stepping into my first internal pentest project at work soon — and this series was the perfect warm-up. Let’s go.