← Writeups

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!

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:

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!