Flagvent 2025: Day 3
FV25.03 - Loop-de-Link
Difficulty
easy
Categories
misc linux
Description
It's time to load the presents into Santa's Slegih at the North Pole, a task assigned to Jinglebell this year. Jinglebell keeps the password for the warehouse safely in a file in his home directory. Unfortunately, the other elves have played a prank—they've hidden the warehouse password in a tangled maze throughout the workshop. Things are falling behind schedule, and Jinglebell needs your help. Can you navigate the labyrinth and find the password?Solution
Upon SSHing into the Linux box, we’re presented with an Ubuntu 24.04.3 LTS system. Looking around, we find an interesting file in our home directory:
elf@host:~$ ls -lah ~
total 12K
drwxr-x---. 1 elf elf 46 Dec 4 12:38 .
drwxr-xr-x. 1 root root 17 Nov 24 20:52 ..
-rw-r--r--. 1 elf elf 220 Mar 31 2024 .bash_logout
-rw-r--r--. 1 elf elf 3.7K Mar 31 2024 .bashrc
drwx------. 2 elf elf 34 Dec 4 12:38 .cache
-rw-r--r--. 1 elf elf 807 Mar 31 2024 .profile
lrwxrwxrwx. 1 elf elf 16 Dec 4 12:36 warehouse_password -> /workshop/cbamf2We see that warehouse_password is a symlink to /workshop/cbamf2. Unfortunately, we don’t have permission to access the /workshop folder or any files within it.
Remembering the challenge title loop-de-link and the description hinting at a maze, we take an educated guess that we’re dealing with a symlink chain, where one symlink points to another in sequence. Normally, we’d use readlink -f <path> to traverse the chain, but that requires permissions we don’t have. So, we look for other ways to walk this chain.
We run sudo -l to check the sudo permissions for our user:
elf@host:~$ sudo -l
Matching Defaults entries for elf on host:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User elf may run the following commands on host:
(root) NOPASSWD: /bin/cat /workshop/*
(root) NOPASSWD: /usr/bin/stat /workshop/*Nice! We’re allowed to use /bin/cat and /usr/bin/stat on the /workshop folder.
Running the stat command on the first few links reveals:
elf@host:~$ sudo /usr/bin/stat /workshop/cbamf2
File: /workshop/cbamf2 -> /workshop/irtegm
Size: 16 Blocks: 0 IO Block: 4096 symbolic link
Device: 0,171 Inode: 270187127 Links: 1
Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2025-12-04 12:36:19.897048505 +0000
Modify: 2025-12-04 12:36:19.897048505 +0000
Change: 2025-12-04 12:36:19.897048505 +0000
Birth: 2025-12-04 12:36:19.897048505 +0000elf@host:~$ sudo /usr/bin/stat /workshop/irtegm
File: /workshop/irtegm -> /workshop/i1edct
Size: 16 Blocks: 0 IO Block: 4096 symbolic link
Device: 0,171 Inode: 270187126 Links: 1
Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2025-12-04 12:36:19.897048505 +0000
Modify: 2025-12-04 12:36:19.897048505 +0000
Change: 2025-12-04 12:36:19.897048505 +0000
Birth: 2025-12-04 12:36:19.897048505 +0000Obviously, there can be many links in the chain, so we use an LLM to generate a walk.sh script to automate the process:
#!/usr/bin/env bash
set -euo pipefail
cur="${1:-/workshop/cbamf2}"
declare -A seen
while true; do
out="$(sudo /usr/bin/stat "$cur")"
echo "$out"
file_line="$(printf '%s\n' "$out" | head -n1)"
if [[ "$file_line" =~ \-\>\ ]]; then
tgt="${file_line#*-> }"
[[ -n "${seen[$cur]:-}" ]] && { echo "Loop detected at: $cur -> $tgt" >&2; exit 2; }
seen["$cur"]=1
cur="$tgt"
else
echo "Reached non-symlink: $cur"
sudo /bin/cat "$cur"
exit 0
fi
doneRunning this script for about a minute rewards us with our daily flag!
elf@host:/tmp$ chmod +x walk.sh
elf@host:/tmp$ ./walk.sh
...
Reached non-symlink: /workshop/dz4xzp
FV25{ELOOP_ELOOP_ELOOP_ELOOP}Flag:
FV25{ELOOP_ELOOP_ELOOP_ELOOP}