Breakme
Break this secure system and get the flags, if you can.
Recon
Let's start with a nmap scan.
We use Ferroxbuster for a directory scan. It’s evident that the web server in question is operating WordPress.
Visiting the index page, we are just greeted with an Apache2 Debian default page.

We visit the WordPress site and discover that it is a straightforward blog.

We seem to be in the right place http://breakme.thm/wordpress/index.php/breakme/.

Next, we use WPScan to analyze the WordPress application. It reveals that the site is running version 6.4.3, which contains a vulnerability allowing user enumeration.
Initial Access
We continue focusing on the WordPress site and run additional WPScan assessments to extract more useful information. These scans help identify potential vulnerabilities that could be exploited.
WPScan Part I - Enum Credentials
We begin by attempting to enumerate existing users on the WordPress site. Through this process, we successfully identify two users: bob and admin.
Next, we proceed to brute-force the passwords for both users. We manage to successfully crack the password for user bob.
A link in the sample page http://breakme.thm/wordpress/index.php/sample-page takes us to the login window.

We log in using the discovered credentials but realize that the account doesn't have elevated privileges. The admin dashboard is not visible, indicating this user has limited permissions.

We can make changes to our profile and are able to make minor adjustments at the dashboard.

WPSan Part II - Further Enumeration (WPScan API Key)
It looks like we haven't discovered everything yet. Next we run another WPScan, this time with an API key.
This can be obtained free of charge after registering on the next page of WPScan. This will allow us to get our results associated with CVEs.
We run the WPScan using the API key and discover an interesting finding that belongs to CVE-2023-1874, which is WP Data Access <= 5.3.7 - Authenticated (Subscriber+) Privilege Escalation.
We can find out more about the vulnerability in the following post:
So we update our profile first.

We submit the request and intercept it via Burp Suite. We must now add the following parameter, which is not already set.
&wpda_role[]=administrator
We assign ourselves the administrator role. However, if we make an error with the parameter and input it incorrectly, we risk being locked out and losing access to the dashboard, requiring a machine restart to regain access.


Reverse Shell
To establish a foothold, we leverage our elevated privileges to create a reverse shell. Following guidance from Hacktricks, we modify a template page's content with a reverse shell script, utilizing revshells.com to generate a suitable Pentest Monkey reverse shell. We ensure this process runs in the background while setting up a listener to catch the incoming connection.

Next, we reach out to the following Page to update a template for a reverse shell.
Here we first set the template to Twenty-Twenty One, because this has a PHP template for the 404 page. We select this and then replace everything with the reverse shell content and then update the file.

Now we only need to call up our edited page with the following URL:
http://breakme.thm/wordpress/wp-content/themes/twentytwentyone/404.php
We then get a reverse shell on our listener and upgrade our shell. We are www-data, but don't have access to the first flag for the time being.
First. Let's upgrade this shell.
We transfer linpeas to the machine and run it.

After our Linpeas scan we find access possibilities to files of other users john and youcef.

We find the first flag in john's home directory, but have no access to it. We may have to get access to user john.
Reverse Shell as john
When enurering using www-data, we detect an internal service running on port 9999.
This seems to be another site, possibly an entry point to user john.
Before we continue, let's take a look at possible processes running in the background using Pspy. Here we see that the service is a web server running in the context of the user with the uid 1002.

This is our user john.
We now want to investigate the service on port 9999 further. To do this, we create a tunnel using Ligolo-ng to gain access to it.
Setup Ligolo-ng
First, we set up a TUN (network tunnel) interface named "ligolo" and configuring routes to forward traffic for 240.0.0.1 through the tunnel.
Next, we download the latest release of ligolo-ng. The proxy and the agent are in the amd64 version.
On our attack machine, we start the proxy server.

Next on the target machine we start the agent to connect to our proxy.

We receive a message on our ligolo-ng proxy that an agent has joined. We select the session using session and then start it.

We are now able to reach internal port 9999 via the address 240.0.0.1.
Here we have a page with tools that include a check target, a check user and check file. This suggests that some kind of command injection could be possible.

If we enter our IP at Check Target, we find that a ping is actually executed. The input only allows the numerical representation of IP addresses.

Check user reflects the entries you have made. However, we cannot find a valid user, not even under the known john, bob or www-data.
The file check does not seem to find any files either. Special characters or numbers do not seem to be permitted here.
We enter a set of special characters in Check User and see that a small set is reflected. Not everything is removed. We also notice that the space character is removed.
Copy
With the character set of special characters that we determined earlier, we can try the following command injection. We use the ${IFS} variable to replace the space, pipe that ping command to the previous command as output.
|ping${IFS}10.17.15.155
We then capture the pings via tcpdump and see that our command injection was successful.

Next, we prepare a simple reverse shell payload. Since we can't use & we use curl to distribute our reverse shell and execute it in the same command.
Then we set up a Python web server to provide the payload.
We replace our ping with a curl command, to see if we can successfully request the payload.
Copy

Now we set up a listener on port 4446 and adapt our command with a pipe to bash.

This gives us our first flag.

Reverse Shell as Youcef
In the home directory of youcef we find that we have access to other files, including readfile.
We want to take a closer look at the files and set up a python web server in the home directory to access these.
Initially, we decompile the readfile binary since we lack access to readfile.c. This binary has the SUID bit set, allowing us to read files with the privileges of its owner, Youcef. We hope to retrieve Youcef's SSH key. However, attempts to read the file result in a "file not found" error, and our access to readfile.c returns a Nice Try! message, indicating restrictions in place.
After decompiling the binary, we discover that it includes checks to verify if the user is john by searching for the UID. It also prevents access to specific filenames like flag and id_rsa, resulting in a Nice Try! message if those are attempted. Additionally, the program checks for symlinks, which if opened, also fail, along with any files to which user john does not have access.
The application first checks if an argument is supplied, otherwise it exits with a usage message. It then verifies if the file exists, exits if it doesn't, and ensures the user is running with UID 1002 (john). If the filename contains flag or id_rsa, or is a symlink, access is denied. However, if the file passes these checks, the program waits briefly (usleep) before opening and printing the file. This delay introduces a race condition, allowing exploitation between the file check and access (TOCTOU vulnerability).
Race Condition
To exploit the race condition vulnerability, we can create a regular file and rapidly toggle it between a regular file and a symlink pointing to the desired file (e.g., youcef’s file). The goal is that during the application's check, it detects the regular file and allows access. But by the time the program opens and reads the file, the symlink points to the target file we wish to access, bypassing the restrictions.
For this, we will first use a loop to constantly switch the file between these two states and run it in the background.
Now, we will create another loop that continuously runs the program, hoping to win the race condition. If we succeed, it will print the output and exit.
As we can see, after a while, we win the race and manage to read /home/youcef/.ssh/id_rsa.

SSH Connection
Now that we have the SSH key, we encounter an additional obstacle: the key is encrypted with a passphrase. To gain access, we will need to crack or bypass this passphrase in order to use the SSH key and establish a shell on the target system.
We can attempt to brute-force the passphrase. First, we need to convert the SSH key into a format that John the Ripper can work with.
We do this using the ssh2john utility, which transforms the SSH private key into a hash that John the Ripper can recognize and attempt to crack.
After converting the key, we can run John to start brute-forcing the passphrase and potentially gain access to the decrypted SSH key.
Now, using john to crack it, we obtain the passphrase.
Using the SSH key and the passphrase we found, we successfully access the system as the user "youcef."
With this shell access, we can now explore the system further and locate the second flag.
By having control over the "youcef" account, we can read files and execute commands under this user, allowing us to progress further in the challenge.
This gives us our 2nd flag.

Privilege Escalation
Executing the program reveals that we are operating within a Python jail, which prompts us for input.
Upon testing the input, we discover that entering invalid Python code triggers a "Wrong Input" message. .
When we input valid Python code, it executes successfully, indicating that our input is likely being passed directly to the exec function.
However, if we try to import a module to run commands, we see the message Illegal Input and the program exits. This indicates that there must be some filtering in place.
Since directly spawning a shell or utilizing typical programs for this purpose is restricted, we should explore alternative methods to gain shell access. We might consider leveraging scripting techniques, utilizing file redirection, or finding executables that allow indirect access. Exploring environment variables or existing permissions could also present opportunities for executing commands that would lead to a shell.
Looking for common Python jail bypass payloads, we find the following payload here. It imports the os module and calls the system function from it:
__builtins__.__import__("os").system("ls")
But, if we try it in our case, we see that it fails.
To begin our exploration, we first attempt to access the import function using __builtins__.__import__. However, we quickly discover that this functionality is restricted.
By breaking down our payload and testing its individual components, we determine that the problem originates from the __import__ function and the quotation marks.
To bypass the issue with quotation marks, we can use single quotes (').
For the __import__ function, instead of calling it directly as __builtins__.__import__, we can access it through the dictionary method with __builtins__.__dict__['__import__'].
This approach allows us to provide __import__ as a string, which we can manipulate using various string methods to circumvent filters. For instance, the application accepts __IMPORT__, enabling us to modify it to __import__.
For instance, we see that the application has no issue with __IMPORT__.
We can utilize __IMPORT__ and then apply a method to convert it to __import__. One method we can use is lower(), which transforms all uppercase letters into lowercase.
Testing the parts of our input once more, we find that lower is also not allowed.
Looking for alternatives to the lower method, we find the casefold method, which serves a similar purpose. As we can see, this method is not filtered and works.
Returning to our payload, we find that when we attempt to import the os module, it is also not allowed, and we can see the reason why: os is filtered as well.
Since os is already supplied as a string and OS is not filtered, we can use the casefold method once more to bypass it.
As we can see, this works, and we are able to access the os module
However, if we try to access the system function, we find that we fail once more.
This time, it’s because system is filtered.
Now, we can use __dict__ once more to be able to use system as a string and apply the casefold method to bypass the filter.
As we can see, this allows us to access the system function successfully.
By using the payload __builtins__.__dict__['__IMPORT__'.casefold()]('OS'.casefold()).__dict__['SYSTEM'.casefold()]('/lib/yorick/bin/yorick'), we can spawn the Yorick interpreter. Once inside the Yorick environment, we can execute the command system "bash" to launch a shell as the root user. This allows us to access and read the third flag, leveraging the vulnerabilities in the Python execution context.
We get our root flag.

Last updated
Was this helpful?