Robots
A (small) tribute to I. Asimov.
Recon
We begin with an Nmap scan and discover three open ports: port 22 running SSH, and ports 80 and 9000 hosting web servers. The default script scan also reveals the entries in robots.txt.
The robots.txt file reveals several interesting directories:
When we visit the index page of the site on port 80, we receive a 403 Forbidden response.

On port 9000, we’re presented with the default Apache2 landing page.

We check the directories listed in robots.txt, but most return a 403 Forbidden error—except for /harm/to/self, which displays a login page.

We access the login page and note that it's a PHP-based form. Unfortunately, it provides no feedback upon submission, making it difficult to enumerate valid usernames.

We use feroxbuster to enumerate additional directories and PHP pages under /harm/to/self. Among the results, we find admin.php, but access is restricted without proper authorization.
We proceed to register a new user by providing a username and date of birth. The system informs us that the initial password is generated by taking the MD5 hash of the username concatenated with the day and month of the date of birth.

We calculate our hash with CyberChef and login.


After logging in, we’re directed to the index page of /harm/to/self. Our username is reflected on the page, and we also notice indications of an Admin user. In the top-left corner, there’s a link labeled Server Info.

Clicking the Server Info link reveals the phpinfo() page. We'll soon explain how this information can be highly useful for our purposes.

Initial Access
XSS via Username
Since the username is reflected on the page, we test for XSS by registering a user with a simple payload as the username. After logging in, the payload executes successfully, and we receive our alert.

If the admin also interacts with the page and views the list of usernames, our XSS payload could execute in their browser. This would allow us to steal the admin's cookie, giving us access to their session and enabling interaction with admin.php, which we previously discovered during the gobuster scan.
Unfortunately, the HttpOnly flag is set on the cookie. This flag prevents JavaScript from accessing the cookie directly, making it accessible only through HTTP requests—specifically to protect against XSS-based cookie theft.

However, we have an alternative method to access the cookie, as demonstrated in the following article. As previously mentioned, the phpinfo() page could play a crucial role in this approach.
We can exploit the phpinfo() page to steal the cookie, leveraging the detailed request data it displays—such as headers and environment variables—when accessed with our crafted XSS payload.

We use a script from HackTricks to exploit this technique, hosting it on our own web server. The script triggers the admin's browser to send a request to the phpinfo() page, including their session cookie, which is then exposed in the server logs or request details.
After modifying xss.js, we observe the first indication of success: a request is made to our server to fetch the xss.js file, confirming that the XSS payload has been executed in the admin's browser.
Next, with our listener running on port 81, we capture the exfiltrated phpinfo() contents, which include valuable details such as session cookies and other sensitive server information.
We save the base64-encoded data parameter from the response to a file and then decode it to extract the information, which might include the session cookie or other useful data.
By opening server_info.html in a browser, we confirm the captured PHPSESSID, which is the session ID of the admin, allowing us to hijack their session.

PHPSESSID=ign959419do8tqiqsleg9i5jed
Using the stolen PHPSESSID cookie, we modify our session cookie and navigate to http://robots.thm/harm/to/self/index.php.
We successfully log in as admin, but the dashboard appears unchanged, indicating that we may need to access more specific admin functionality, like admin.php, to see a difference.

Remote File Inclusion
During our feroxbuster scan, we discovered admin.php. Let’s try accessing it now to see if it reveals any admin-specific functionality or content.
When navigating to http://robots.thm/harm/to/self/admin.php, we encounter a form that allows us to submit URLs, likely for admin-related functionality.

To test the form, we submit a URL pointing to our own web server (http://10.17.15.155/test), observing how the system handles external requests.
We successfully observe a request being made to our server, confirming that the submitted URL is being fetched—likely server-side—by the admin interface.
The admin.php page returns an error message indicating that our submitted URL was passed to the include() function, strongly suggesting a Remote File Inclusion (RFI) vulnerability.

Since Remote File Inclusion (RFI) is possible, we host a simple webshell on our server to be included and executed by the target when we submit its URL.
We submit the URL of our webshell (http://10.17.15.155/cmd.php) to the admin.php form along with a command, such as cmd=id.
Shortly after, we observe a request to cmd.php on our server, confirming that the target has fetched and likely executed the webshell.
And we can see the output of the command directly in the response.

To gain an interactive shell, we prepare a reverse shell payload hosted on our web server, which the target will include and execute via the RFI vulnerability.
We then use the same RFI method to include our webshell and execute the command:

This fetches and runs our reverse shell script, establishing a connection back to our listener.
Shell as rgiskard
Discovering Database Configuration
While reviewing the application files, we discover the database configuration inside /var/www/html/harm/to/self/config.php, which may contain critical information such as database credentials and connection settings.
Connecting to the Database
From the configuration, we learn that the database is running on the db host. Using the getent command, we can retrieve the IP address associated with the db host:
Since the mysql client is not installed in the container, we can use chisel to set up port forwarding and connect to the database from our local machine.
Set up a chisel server on our local machine:
Next, we transfer chisel into the container using curl:
We forward the database port using chisel:
With the database now accessible from our local machine, we can connect to it, list the tables, and extract the stored user password hashes.
Cracking the Hash
Now that we have the hash for the rgiskard user, we can try to crack it. From our earlier observations on the webserver, we know that passwords follow the format md5(username + DDMM).
Reviewing login.php, we see that this value is hashed again with md5 before being compared to the database. Therefore, the hashes stored in the database follow the format:
md5(md5(username + DDMM)).
With this knowledge, we can write a Python script that brute-forces all possible day and month combinations (i.e., values from 0101 to 3112) for the date of birth, constructs the password as md5(md5(username + DDMM)), and compares the result to the hash retrieved from the database.
Running the script, we successfully discover the password for the rgiskard user.

Shell as dolivaw
Although the plain password doesn't work, we can use the MD5 hashed password with SSH to gain shell access as the rgiskard user on the host.
After checking the rgiskard user's sudo privileges, we discover that they are allowed to run the /usr/bin/curl 127.0.0.1/* command as the dolivaw user.

From the sudo configuration, while the first URL we pass to curl must be 127.0.0.1/, curl accepts multiple URLs in a single command. Combining this with the file:// protocol, which curl also accepts, we can simply read the user flag as follows:
From the sudo configuration, while the first URL we pass to curl must be 127.0.0.1/, curl accepts multiple URLs in a single command. By combining this with the file:// protocol, which curl also supports, we can easily read the user flag like this:

To get a shell as the dolivaw user, we can use curl's -o option to save the responses from the requests to a file. This allows us to write our public SSH key to the user’s authorized_keys file.
Here are the steps:
Generate a key pair (if you don't already have one):
This will create
id_ed25519(private key) andid_ed25519.pub(public key).Serve the public key from your web server. If you're using a simple HTTP server (for example, Python's
http.server), navigate to the directory whereid_ed25519.pubis located and start the server:This will serve the public key at
http://<your-ip>:8000/id_ed25519.pub.Use
curlto fetch the public key and then we can add todolivawuser'sauthorized_keysfile:Ensure that the
/home/dolivaw/.ssh/directory exists and has the correct permissions (you might need to create it first if necessary).SSH into the
dolivawuser using the private key (id_ed25519):
This will allow you to get a shell as the dolivaw user by using the SSH key you added to the authorized_keys file.

Privilege Escalation
After checking the sudo privileges for the dolivaw user, we discover that they are able to run /usr/sbin/apache2 as the root user. This gives us the ability to control and configure the Apache2 server.

There are several ways we can leverage Apache2 to either read the root flag or gain root shell access.
The simplest method, which is mentioned below allows us to easily read the root flag.
Apache2 lets us specify directives either through configuration files or command-line arguments. We can make use of the Include directive, which includes additional configuration files. Here’s the key point: if we try to include a file that doesn't contain valid directives, Apache2 will simply print an error along with the contents of the included file.
We can exploit this behavior by including the root flag, as it obviously won't contain valid directives, and Apache2 will output its contents as part of the error message.

As we can see, when attempting this, we encounter an error because APACHE_RUN_DIR is not defined. However, this isn't an issue for us, as we can easily define it using another directive. Once we do that, we can successfully include our file, and Apache2 will print the contents of the root flag.

Last updated
Was this helpful?

