Moebius
A place where you start at some point, and you have to go back to it in the end. - by shamollash
Last updated
Was this helpful?
A place where you start at some point, and you have to go back to it in the end. - by shamollash
Last updated
Was this helpful?
The following post by 0xb0b is licensed under
We start with a Nmap scan and find only two open ports. Port 22
on which we have SSH available and an Apache web server on port 80
.
We first visit the index page of the web server and find an image board with pictures of cats. We can choose from three categories.
If we select a category, we are redirected to the page album.php
with a set parameter short_tag
of our category.
If we look at the source of the page, we find a comment. Probably a little hint. The short_tag
fav
has the album ID 3
and we can also see the links to the images. They look very interesting.
At first glance, these look like they are vulnerable to Local File Inclusion (LFI) via the path parameter. We need to provide a hash each time. We do not know how this is generated. It could be the path itself or the content. We use hashid
to determine that it might be SHA-256. The parameters do not appear to be SQL injectable. Omitting the hash value does not result in an LFI, the file wont then be found. Either it is not SHA-256 and the hash is determined by the path, or the hash is generated from the content. More on this later.
However, if we leave the short_tag
parameter blank of album.php,
we get a useful error message indicating that the parameter is vulnerable to SQL injection.
We'll make it easy for ourselves and leave it to SQLMap to find out more.
We see that the parameter is vulnerable to three different SQL injection variants:
We don't worry about this at first, but we notice that a UNION injection is not possible to leak data.
We dump the database...
... and are able to dump the tables albums and images of the database web. We can see from the dump that there are no hashes stored there. These are probably generated. This gives us hope for the LFI. If we could now write paths to the database, or use a UNION injection to render a path to http://moebius.thm/album.php
, we could use the LFI.
When testing manually, you will notice that certain special characters are filtered. These include ;
and /
. This now makes it more difficult for an LFI. Since ;
is not allowed, we cannot execute stacked queries, and therefore no INSERT INTO
. Furthermore, filtering /
makes it difficult for us to inject paths.
At least we could bypass the /
filter using hex encoding.
We try to apply LFI in order to obtain possible RCE.
The whole challenge might be solely about being able to include files via the image.php
page.
But we need the hash. It's either a hash of the path or of the contents of the file.
The idea is now to fake a path or insert a path in the images table to read arbitrary files.
Unfortunately, we cannot use insert since stacked queries are not possible with ;
being filtered.
Let's see if we can read or write to files using SQL.
First, we use sqlmap and intercept the requests made to adapt those to our liking. And to learn what's actually happening under the hood of sqlmap. We decide to intercept the error-based injections.
Error-based SQL injection works by forcing the database to produce an error message that leaks data directly in the response. Sqlmap, when using error-based techniques, automates this by crafting special payloads that cause database errors containing the data it wants.
We can see that the payloads error using RAND()
collisions with a GROUP BY
, which causes MySQL to print.
Below is an adapted request to check for the permissions of our database user.
Checking the user privileges we are also not able to read or write files, we only have USAGE
permissions:
The INFO
column contains the statement that the thread is executing.
We adapt the query to see the actual query running.
We see that SELECT id FROM albums WHERE short_tag=...
is queried. This explains why a UNION Injection is not working here. There might be another query that uses the ID by the former query. With that in mind, we might be able to make a nested UNION Injection to fake the paths, getting a hash calculated, and a URL prepared to read the files.
So the idea is to make a UNION injection that results in an ID containing another UNION Injection passed to the second query.
The second query that uses the ID might look like something like this:
A nested query could then look like this:
So the first query passes the nested UNION to the second query.
This would then result in the following second query:
However, this does not work directly, as " UNION SELECT '/etc/passwd' -- -"
would be evaluated in the first query. But in order to circumvent the earlier execution, we can hex encode the payload.
Before we start with UNION injection, we want to determine the column count used in the second unknown query. For this, we use ORDER BY. We keep increasing the value by 1 until we receive an error.
We encode the to be nested ODER BY query to hex.
And integrate that into our UNION injection. We do not receive an error.
After increasing the ORDER BY
to 4 we receive an error. We have three columns.
Next, we try the following payloads, to UNION inject a path and determine which column is actually the path column:
And we will be successful with:
Those are the steps we need to take. Encode the inner UNION SELECT to hex.
Inject our payload...
... and we receive the following link from the page. The file inclusion is working. We are able to inspect the /etc/passwd
file.
We were unable to find any flags on the machine . As a small note, when looking at the /etc/hosts
file, it is noticeable that we may be in a Docker container.
Let's inspect the PHP files; perhaps there are other hidden secrets or even flags. Let's take a look at album.php
; maybe we can see how the hash is generated and whether we still need the SQL injection.
We cannot see the contents of the PHP files directly because they are being evaluated. We can try to encode them base64 with a PHP filter php://filter/convert.base64-encode/resource=
. We'll see that it works, and it confirms that the path will be hashed.
We prepare the inner UNION SELECT.
Paste the hex encoding into our payload '+UNION+SELECT+REPLACE+--+-
, and make our request.
We receive the link and are able to read album.php
.
We use CyberChef for decoding. The album.php
page includes dbconfig.php
. We will look at this next. We also see that the hash is an HMAC, the secret used cannot be found in album.php
. We will probably find this in dbconfig.php
.
We repeat our steps to now read dbconfig.php
.
This contains the secret.
Using the secret, we can now calculate the hashes ourselves and no longer need the SQL injection.
Example for /etc/passwd
With LFI, we have several possibilities to gain RCE. This would include log file poisoning or cookie poisoning by writing our PHP payload to logs or cookies. However, we do not find any logs such as /var/logs/apache2/access.log
or cookies on the machine; none are set, and no content is defined that we could manipulate.
In rare cases, you could also use living of the land sscripts like /usr/local/lib/php/pearcmd.php
to achieve RCE if arguments can be passed via HTTP. But this setting was not set.
But we still have the option of using filter chains to get RCE.
With Filter Chains we are able to generate arbitrary php code for the include without needing to write it into a file. This allows us to gain RCE.
We will use the following generator by synacktiv:
Let's try to get a web shell using the smallest possible payload `'<?=`$_GET[0]?>
. We generate the filter chain and save it to webshell-payload.txt
. The resulting payload will be pretty big and almost surpasses the URL size limit.
Next, we calculate the hash with our script.
We request album.php
with the chain as the path and save the response to webshell.php
.
We read the response and see an error. The shell_exec()
function is aliased by the backticks, which
is not definied. That's strange; it could be that some functions are disabled, which we need to get RCE. At this point, trying bigger web shell payloads will not suffice since the URL will be too big by the chains...
Let's make a check, and see if we can display the information about the current PHP environment using phpinfo()
.
We prepare the chain:
Calculate the hash for the path:
We request album.php
with the chain as the path and save the response to phpinfo.php
. The content looks good, no errors.
Let's check for functions...
... and we see at least the following are disabled. We are somewhat very limited.
But there are solutions for this, we can try to bypass the disabled functions. After some research, we'll come across Chankro.
TarlogicSecurity explains it as follows:
PHP in Linux calls a binary (sendmail) when the mail() function is executed. If we have putenv() allowed, we can set the environment variable "LD_PRELOAD", so we can preload an arbitrary shared object. Our shared object will execute our custom payload (a binary or a bash script) without the PHP restrictions, so we can have a reverse shell, for example.
We generate an example and see that it is a lot of code. We won't be able to pass that into our filter chain because of the limits of the URL size.
The first idea is to place that PHP script on the target system using file_put_contents
using filter chains, but that results in a too large URL, which won't be possible to request.
Now we need a workaround... And we try it with the smallest possible payload that evaluates PHP. And receives what it is supposed to evaluate via POST request. This is how we get out of the limitation.
We generate the filter chain.
Calculate the hash for the path (filter chain).
And make a test request with echo 'hello world
as the POST data. And we see it gets evaluated.
Now we could issue something like -d "x=file_put_contents
in our POST request to write the Chankro script to the target system.
First, we prepare a reverse shell that is used by Chankro.
We generate our Chankro script. The path is set to /tmp
there we have write permissions. First, we have tested with /var/www/html
where we were not able to write.
And prepare a small script to base64 and url encode the content for an error free transmission.
We run our script to encode the Chankro payload.
Next, we write the Chankro script to /tmp
using file_put_contents('/tmp/chan.php', base64_decode('$(cat chan.php.b64)'));
After we have written the Chankro script to tmp
we set up a listener on our desired port and request the script via LFI. We calculated the hash with our script.
We get a connection back and are www-data
. We won't find any flags inside the container.
Next thing we do is to upgrade our reverse shell.
We see we are in the sudo group.
We can easily switch to root
using sudo su
without a password.
Next, we run linpeas and see that we are either able to performcore_pattern breakout
or an uevent_helper breakout
to escape the docker container.
Recalling T3 from AoC 2024:
A Docker core_pattern
breakout leverages the misconfiguration of the core_pattern
kernel parameter in a containerized environment to break out of a Docker container and escalate privileges on the host. This is possible when the host system is configured to store or process core dumps in a way that exposes sensitive data or allows interaction between the container and the host.
Privileged Container: The container must be running in privileged mode or have CAP_SYS_ADMIN capabilities to modify /proc/sys/kernel/core_pattern
.
Writable Host Filesystem: Access to write to host directories (e.g., via mounted volumes).
Vulnerable Configuration: The host's core_pattern
is configured insecurely or can be overridden by the container.
An example can be found here:
If we can successfully write our script to /proc/sys/kernel/core_pattern
prefixed with a pipe, the kernel will execute our program outside of the container.
Since we do not have Python available on the container we use the following C snippet to trigger a SEGFAULT.
We can show the mounts and find the overlay folder. If we write to /
on the container, it is actually at /var/lib/docker/overlay2/7b3cf26f528efa374bb00eda5e44378ccee75cbcf5a9c9f82764191ad6c0d474/diff
We place our shell.sh
in /
of the docker container.
We write the path to the shell script prefixed with a pipe to execute it outside the container.
With the following poc, we force a crash.
And get a connection back to our listener.
We are root
on the host and find the user flag at /root/user.txt
.
We are still missing the root flag. First we upgrade our reverse shell.
... go back to where you began, in order to find the root flag
The challenges asks us to get back to where we began. And we began initially with a SQL Injection. Maybe we have to look for the final flag in the DB container, since we did not find a DB service running on the compromised container. Not actually a root flag, but a final flag.
We check for running docker containers and spot the challenge-db-1 container.
We interact with it.
On the root directory we find a suspicious folder containing the database environment file. This holds the credentials of the web and root user of the database.
Connecting to the database we get the same results as expected as with our dump.
We use the root credentials and we now have access to more databases. Among theses secret.
We use the database secret and dump the contents of the table secrets. This contains the final flag.
Using hex encoding, we were able to use the ;
character to insert into the database. With this approach, we could enable LFI by requesting an album with images whose path is set to the desired files.
Before attempting the LFI to RCE technique, another approach was tried: writing a webshell payload into the path field in the hope that it would be rendered. However, it was not.
When accessing , all entries are loaded and the hash values are generated; the hashes are not stored in the database.
Let's see what is actually being queried when http://moebius.thm/album.php?short_tag=cute
is queried. To do this, we check the entries in information_schema.PROCESSLIST
- recalling .
The INFORMATION_SCHEMA
has the table PROCESSLIST
. The PROCESSLIST
table contains information about running threads.