BSides Toronto CTF 2026 - Network
Solving challenges Network 1 and Network 2
In this writeup I detail how I worked through the Network challenges of BSides Toronto’s 2026 CTF. A TLDR of the process: scanning an internal network, finding a readable NFS share, leaking an OpenSSH private key, and exploiting permissive sudo commands.
The challenge
The challenges provide us with a .ovpn file and the simple description All hosts are in the 10.0.1.0/24 range.
Connecting to the VPN
Attempting to connect to the VPN with sudo openvpn ./client.ovpn kept giving a certificate error and restarting, specifically:
Certificate does not have key usage extension
I am not sure if this was intended as a part of the ctf or simply a misconfiguration, but simply commenting out the remote-cert-tls server line in the config itself by prepending it with a semicolon was enough to get the VPN to work.
Running a network scan
ip a while connected to the VPN showed a new network interface tun0 with assigned IP 10.0.252.134. The instructions specifically mention that we should look at the 10.0.1.0/24 range, so the first thing that came to mind was to see what hosts were even live within that range.
sudo nmap -sn 10.0.1.0/24 # -sn disables the port scan to execute only a ping scan
This revealed 4 live hosts:
10.0.1.1
10.0.1.51
10.0.1.138
10.0.1.176
Amazing! Now we know what machines we’re even working with. But I still had no idea what services were running so that I could interact with them, so a scan for the 1000 most common ports would help me find out:
sudo nmap -Pn -sC -sV -oA nmap/nmap-results -iL live_ips.txt
-Pn disables the initial ping, since we already know these hosts are live, -sC runs Nmap’s default scripts, which usually add details (not worried about stealth because it’s a CTF), -sV to detect versions of services, and -oA to output results in 3 useful formats to files. It’s easier to have the output for reference and not have to scroll through the terminal to look at the scan results. -iL just sources the IPs from a file with one on each line.
As a side note, while the above command ran and I looked at the results, I also ran an Nmap scan on the same hosts but for all ports, with the flag -p- and without scripts or version checking, just to see if there was anything unexpectedly open (I’ve been bit in the a** before by not checking). This yielded no new ports.
Lo and behold, lots of interesting results!
10.0.1.1filtered all ports, probably just the gateway and not meant for us.10.0.1.5111 ports shown, which we will get to in a second since they were the most interesting.10.0.1.138and10.0.1.176had just port22with SSH open and9090with something calledzeus-adminclosed. A quick google search revealed thatzeus-adminis something that Nmap often aliases port 9090 to but may not actually be that service. Trying to connect to the port with telnet showed nothing, and since it was closed anyways I dismissed it for now (keeping it in the back of my head in case it was useful later).
I tried quickly SSHing into all three hosts since they had port 22 open, using root and empty passwords or just some basic ones (you never know), but no luck. So I started looking deeper into everything running on 10.0.1.51.
Looking deeper at our main server
The results of the scan on 10.0.1.51 are as follows:
Nmap scan report for 10.0.1.51
Host is up (0.014s latency).
Not shown: 973 filtered tcp ports (no-response), 16 filtered tcp ports (admin-prohibited)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.9 (protocol 2.0)
| ssh-hostkey:
| 256 2c:e6:f8:7d:be:c6:86:70:9b:16:e4:0a:0e:13:00:3f (ECDSA)
|_ 256 46:58:19:0a:7f:62:ee:51:46:fd:40:6b:79:13:73:88 (ED25519)
53/tcp open domain ISC BIND 9.16.50 (RedHat Linux)
| dns-nsid:
|_ bind.version: 9.16.50-RH
80/tcp open http Apache httpd
|_http-title: Did not follow redirect to https://freeipa.bsidestoctf.com/ipa/ui
|_http-server-header: Apache
88/tcp open kerberos-sec MIT Kerberos (server time: 2026-10-03 17:42:07Z)
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100000 3,4 111/tcp6 rpcbind
| 100000 3,4 111/udp6 rpcbind
| 100003 3,4 2049/tcp nfs
| 100003 3,4 2049/tcp6 nfs
| 100005 1,2,3 20048/tcp mountd
| 100005 1,2,3 20048/tcp6 mountd
| 100005 1,2,3 20048/udp mountd
| 100005 1,2,3 20048/udp6 mountd
| 100021 1,3,4 33509/tcp nlockmgr
| 100021 1,3,4 41630/udp6 nlockmgr
| 100021 1,3,4 42116/udp nlockmgr
| 100021 1,3,4 44429/tcp6 nlockmgr
| 100024 1 33789/tcp6 status
| 100024 1 37351/tcp status
| 100024 1 48715/udp6 status
| 100024 1 49707/udp status
| 100227 3 2049/tcp nfs_acl
|_ 100227 3 2049/tcp6 nfs_acl
389/tcp open ldap (Anonymous bind OK)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=freeipa.bsidestoctf.com/organizationName=BSIDESTOCTF.COM
| Subject Alternative Name: DNS:freeipa.bsidestoctf.com, othername: UPN:ldap/freeipa.bsidestoctf.com@BSIDESTOCTF.COM
| Not valid before: 2026-09-25T03:37:06
|_Not valid after: 2028-09-25T03:37:06
443/tcp open ssl/http Apache httpd
| tls-alpn:
|_ http/1.1
|_http-server-header: Apache
|_http-title: Did not follow redirect to https://freeipa.bsidestoctf.com/ipa/ui
| ssl-cert: Subject: commonName=freeipa.bsidestoctf.com/organizationName=BSIDESTOCTF.COM
| Subject Alternative Name: DNS:freeipa.bsidestoctf.com, DNS:ipa-ca.bsidestoctf.com, othername: UPN:HTTP/freeipa.bsidestoctf.com@BSIDESTOCTF.COM
| Not valid before: 2026-09-25T03:37:38
|_Not valid after: 2028-09-25T03:37:38
|_ssl-date: TLS randomness does not represent time
464/tcp open kerberos-sec MIT Kerberos (server time: 2026-10-03 17:42:07Z)
636/tcp open ssl/ldap (Anonymous bind OK)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=freeipa.bsidestoctf.com/organizationName=BSIDESTOCTF.COM
| Subject Alternative Name: DNS:freeipa.bsidestoctf.com, othername: UPN:ldap/freeipa.bsidestoctf.com@BSIDESTOCTF.COM
| Not valid before: 2026-09-25T03:37:06
|_Not valid after: 2028-09-25T03:37:06
2049/tcp open nfs_acl 3 (RPC #100227)
9090/tcp closed zeus-admin
Service Info: Host: BSIDESTOCTF.COM; OS: Linux; CPE: cpe:/o:linux:linux_kernel
That’s a lot. Let’s look at them one by one:
22/tcp. SSH, already looked at it53/tcp. DNS, running dns queries withdigagainst it suggests that it forwards DNS80/tcp. Aha, a HTTP server, with a new domain and subdomainfreeipa.bsidestoctf.com(the domain the ctf is hosted on isctf.bsidesto.com, notice the missingctfafterbsidesto). A quick search shows that FreeIPA is a service similar to AD but specific to Linux systems. We will come back to this service in a bit.88/tcp. Kerberos, good to know111/tcphas rpcbind open, and the list of services running mentions nfs. This will be incredibly useful in a second.389/tcprunning ldap, and the ssl cert suggests an organization name ofBSIDESTOCTF.COM. Maybe a domain for the local directory?443/tcprunning the same http server through TLS. The cert shows another subdomainipa-ca.bsidestoctf.com.464/tcpalso kerberos, but specifically for setting new passwords636/tcpldap over ssl2049/tcpnfs_acl. This is the second time NFS has been mentioned.
This was already a lot of information, and presented a few different pathways to follow. I could either look further into the web server, kerberos, or the RPC/NFS service.
I tried to start with the web server by adding the freeipa domain to my /etc/hosts and accessing it from the browser, but it wouldn’t load. I got stuck here for longer than I would like to admit, not being able to figure out why the URL wasn’t loading, and later realised that I had simply typed in the url freeipa.bsidesto.com instead of freeipa.bsidestoctf.com. Maybe sometimes it would be better to copy and paste :)
While I was stuck and before I discovered this typo, I decided to take a break and look into the NFS shares.
Exploring NFS
Looking into my personal notes on NFS I remembered the showmount command to simply show available NFS shares, and bingo! The specific command I used was:
showmount -e 10.0.1.51
and this showed that I could mount /export/home and /export/backup/home. Trying them in order, /export/home worked but I did not have permission to read the files. Instead, the following command mounting the backups folder gave me files that I could read:
sudo mount -t nfs 10.0.1.51:/export/backup/home /mnt -o nolock
There were a few folders with very interesting names:
admin ctfadmin janedoe johndoe loguser restricteduser testuser
I usually run tree when faced with a bunch of directories, just to get a feel for how deep they go and what I have to work with. In this case it showed an absurd number of directories and files nested in all the folders, with names such as testuser/dir_7bd36d390dde/file_4cc379052973.bin and varying file extensions.
There were 151 files in total, a lot of them being binary data, and it felt like a waste of time to look through every single one. I decided to take a shot in the dark and use ripgrep (one of my favourite cli tools, a better grep) to look for the flag.
rg flag
The only result was exactly what I was looking for, and I later found out that it was the only ASCII file in there anyways!
network 1 - flag{REDACTED}
A rabbit hole that might come in useful later
Once I had found the first flag, I assumed that I was done with the NFS shares (foreboding), so I switched back to the web server. Realising that I had entered the wrong URL earlier, I fixed it and saw a login page for FreeIPA on CentOS. The authentication options were interesting.
There was a username and password field with a login button, standard. There was also a button saying Log in Using Certificate and another, Sync OTP Token. The only other thing on the page were instructions on the right, explaining how to use the login options. The option that piqued my curiosity the most said the following:
To log in with Kerberos, please make sure you have valid tickets (obtainable via kinit) and configured the browser correctly, then click ‘Log in’.
It was interesting because we had seen that kerberos was running in the nmap scan. It looked further useful when a quick search for FreeIPA vulnerabilities revealed to me CVE-2026-11861, allowing AD users to bypass authentication through a modified PAC in a TGS ticket. Ticket Granting Service tickets are used by Kerberos to allow a user to access specific services, almost like a movie theater ticket that you show at the front door to get inside. Maybe I could find user credentials somewhere and then modify the ticket to exploit this CVE?
Unfortunately I was not able to move beyond this point in the web server, but this line of thinking did eventually help me progress in an unexpected way.
What’s better, a Kerberos ticket or an OpenSSH private key?
I remembered that I had just found a bunch of files in NFS and had literally only looked at one of them through my ripgrep. What if there was something else useful in there, such as a Kerberos ticket or authentication data?
I decided the easiest way to see all the files would be to simply check for the filetypes first with file. I ended up writing the following command to list all the files in /mnt, run each through file, and put the output in files.txt:
fd | xargs file | sort > ~/ctfs/bsides/files.txt
The output looked something like this (truncated):
admin/: directory
ctfadmin/: directory
janedoe/dir_1c13698841af/: directory
janedoe/dir_1c13698841af/file_01712b952bea.json: OpenPGP Secret Key
janedoe/dir_1c13698841af/file_43a345482e91.json: data
janedoe/dir_1c13698841af/file_78c146a80131.json: data
janedoe/dir_1ee26bc27f7a/: directory
janedoe/dir_1ee26bc27f7a/file_0f320dfb5b22.dat: data
...
Already with a quick glance I could spy an interesting OpenPGP Secret Key, but there were still too many files to want to look through manually, and a lot of them were just data files so nothing I could really look at anyways. I filtered away the files I wasn’t interested in
grep -vE "data|empty|directory" files.txt > interesting_files.txt
-v inverts the search to exclude matches, so I ended up with the more digestible output:
janedoe/dir_1c13698841af/file_01712b952bea.json: OpenPGP Secret Key
johndoe/dir_1c505d2f24a2/file_22226555767c.csv: OpenPGP Public Key
johndoe/dir_4ed36e6a0f2e/file_713a326c1ae6.dat: OpenPGP Public Key
loguser/dir_28a110392599/file_570b32cc155c.json: OpenPGP Secret Key
testuser/dir_1b8f2f1a090e/file_4d281f8d0d31.bin: OpenPGP Public Key
testuser/dir_48bf6aea77c7/file_117f18fc6910.log: OpenPGP Public Key
testuser/dir_48bf6aea77c7/file_607c57d6535c.txt: ASCII text
testuser/dir_66f23c007901/file_4a4e68de15a4.dat: DOS executable (COM), maybe with interrupt 22h, start instruction 0xeb59dbfd 349fe6d5
testuser/dir_7bd36d390dde/file_4cc379052973.bin: OpenSSH private key
testuser/dir_7bd36d390dde/file_59b11efe20ae.json: OpenPGP Public Key
The ASCII file was just the flag we had already found, and I wasn’t able to run the DOS executable, so I was stuck here. A pair of keen eyes might notice that, besides all the PGP keys, there was something there I could still look at, but perhaps I needed to give my eyes a break from the screen by that point because I completely missed it.
I circled back to some other leads here and was stuck for a while, so my teammate asked me to bring him up to speed so he could work on this challenge too. As I was showing him all my terminal output, I briefly showed the filtered output of the filetypes, and he just said “wouldn’t the SSH key be useful?”. I had completely skimmed over it and assumed it was also a PGP key. We could now move forwards!
Finding the second flag
Well, if I had an SSH private key, all I had to do was find the right user and server that it opened up. The usernames that I knew from the NFS share weren’t many, and I only had 3 IPs to test, so I just went through the combinations manually. Finally, I succeeded with testuser at 10.0.1.176. A shell, hoorah!!
There was nothing interesting in the user’s home directory that I hadn’t seen already in the NFS share, so I thought to check for interesting files in other directories, one of them being /tmp.
[testuser@devserver ~]$ ls -la /tmp
total 36
drwxrwxrwt. 13 root root 4096 Oct 4 03:07 .
dr-xr-xr-x. 21 root root 272 Oct 3 02:02 ..
drwxrwxrwt. 2 root root 6 Sep 25 03:00 .font-unix
drwxrwxrwt. 2 root root 6 Sep 25 03:00 .ICE-unix
-rw-------. 1 root root 2270 Oct 3 19:12 krb5ccmachine_BSIDESTOCTF.COM
-rw-------. 1 testuser testuser 3602 Oct 3 20:09 lc_5394
-rw-------. 1 testuser testuser 3583 Oct 3 20:10 lc_5411
-rw-------. 1 testuser testuser 3602 Oct 3 20:10 lc_app
-rw-------. 1 testuser testuser 3583 Oct 3 20:12 lc_app2
-rw-------. 1 testuser testuser 3583 Oct 3 20:14 lc_app3
-rw-------. 1 testuser testuser 3583 Oct 3 20:21 lc_pe
-rw-r--r--. 1 loguser loguser 294 Oct 3 01:42 loguserfile
Having explored FreeIPA earlier, the krb5ccmachine_BSIDESTOCTF.COM file felt like striking gold. Unfortunately, it was owned by root and not readable. However, another file that was indeed readable to me was loguserfile. Running file on it showed that it was a Kerberos Keytab file!
I copied that file to my machine and proceeded to spend a frustrating amount of time trying to work with it and the FreeIPA webpage, to no avail. I tried using it to SSH into the boxes, authenticate into FreeIPA directly, and everything just closed me out. I couldn’t figure out how I was supposed to use this file.
I ended up back on the shell I already had, listing more files that could be interesting. I had ls / written in the prompt and was tabbing through the autocomplete options, when I noticed a directory that I hadn’t seen earlier, /ctf. It had a file called flag that was only readable by loguser.
Fortunately, one of the first things I run when I get a shell on a machine is sudo -l, which had already shown me the following:
User testuser may run the following commands on devserver:
(loguser) NOPASSWD: /usr/bin/cat, /usr/bin/grep, /usr/sbin/find, /usr/bin/less
I could read files with cat as loguser! Even though I knew this from earlier, I hadn’t found any files that would need loguser yet, so I hadn’t known what to do with it. A simple command now did the trick and revealed the flag to me:
[testuser@devserver ~]$ sudo -u loguser /usr/bin/cat /ctf/flag
network 2 - flag{REDACTED}
That’s it
This is all I had time for during this CTF, and I was extremely proud of finding two flags in this network!
As is usual, there are always rabbitholes that one goes down and end up taking way too much time, but I have come to realise that these are also extremely useful. Not only can they sometimes accidentally lead you to finding the correct path, as the Kerberos ticket showed me the SSH private key here, but the process of researching these teaches you things you wouldn’t have looked into otherwise. For example, I now know more about authenticating with Kerberos and how to manipulate keytabs than I did before this CTF!