webfreemedium

Job App Simulator

b01lersc

Task: an Apache CGI Bash application parses a form and checks graduation_year with [[ ... -lt 2026 ]], causing attacker input to be evaluated as arithmetic. Solution: inject an array subscript with command substitution, print /flag.txt through /proc/$PPID/fd/1, and leave the expression as 2026+a[0] so validation succeeds.

$ ls tags/ techniques/
bash_arithmetic_injectionassociative_array_subscript_injectionproc_fd_stdout_exfiltration

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

Job App Simulator — b01lers CTF 2026

Description

No official organizer description was included with the provided challenge files.

We are given an Apache CGI Bash application that accepts an application/x-www-form-urlencoded job application form. The goal is to turn the graduation_year validation into code execution and recover the real flag from /flag.txt.

Analysis

The important part of application.sh is the form parsing and the graduation year check:

declare -A form_data while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do form_data["$(urldecode "$key")"]="$(urldecode "$value")" done <<<"$request_body&" if [[ "${form_data[graduation_year]}" -lt 2026 ]]; then echo "Invalid graduation year" return fi

At first glance this looks like a simple numeric comparison, but in Bash the operands of [[ x -lt y ]] are evaluated as arithmetic expressions. That means attacker-controlled input is not treated as a plain string here.

So if we control graduation_year, we can supply an arithmetic expression instead of a normal year. Bash arithmetic also understands array syntax such as a[0], and array subscripts can contain command substitution. This gives a payload shape like:

2026+a[$(command)0]

If $(command) produces no captured stdout, the expression becomes:

2026+a[0]

In arithmetic context an unset array element evaluates to 0, so the whole expression becomes 2026, which is not less than 2026. The validation therefore passes.

Why /proc/$PPID/fd/1 works

There is one catch: output written to normal stdout inside $(...) is consumed by the command substitution itself and inserted back into the arithmetic expression, which would corrupt it.

The fix is to write directly to the parent shell's file descriptor 1 instead:

/proc/$PPID/fd/1

...