The solution and the writeup provided were written by the hunter: garthoid
Description
The Secret Manager application exposes three separate vulnerabilities that chain together into a single exploit: argument injection via filename into cp, path traversal into the vault directory, and finally a specially crafted inject into the BusyBox implementation of grep. None of these alone is sufficient and the exploit only works because all three fire in sequence within a single request. The result of this change is the exposure, deletion, or modification of highly sensitive information.
PoC
Vulnerability 1: Argument Injection via filename into cp
The filename parameter permits us to inject command line options into the cp command. By providing a space separated list of filenames we are able to bypass limited input validation code to inject parameters into the cp command through two steps in the code.
The following code loops through each filename provided and writes the provided content to it.
1for filename in filenames:2 file_path = os.path.join(UPLOAD_FOLDER, filename)3 with open(file_path, "w") as f:4 f.write(content)
If we provide a filename with the name "-r" we are ready for the next step. Note also is that we can also use this code to overwrite any file in the vault, modifying contents, or destroying data. This leads us to mark this as a HIGH integrity CVSS value.
Consider the code fragment below:
1# Backing up the changes to vaults2os.chdir(UPLOAD_FOLDER)3os.system(f'cp * {VAULT_FOLDER} 2>/dev/null') # ← shell glob
When you provide a file named -r, the shell expands alphabetically. Since -r sorts before regular filenames, the command inserts "-r" before any filenames the glob () finds. Since "-r" is a recursion flag for the cp command which dumps the contents of internal_secrets/ folder into the VAULT_FOLDER.
We are now ready for the next step.
Vulnerability 2: Path Traversal into Vault Directory
The input validation code on the filename variable is insufficient:
1if filename.startswith('/'): # blocks absolute paths2elif '\\' in filename or '..' in filename: # blocks traversal
While the code blocks filenames containing .. or starting with /, it does not normalize paths before use. The current checks are effective against simple traversal but does not protect against other dangerous characters (like -- or -). Our attack payload of "vaults/--" completely pass this input validation and a file called "--" gets written into VAULT_FOLDER. (/tmp/uploads/vaults/--). We will see later how "--" is important in the grep.
Vulnerability 3: Busybox grep does not support --exclude-dir
The developer comment provides a clue:
1# We just moved from GNU to BusyBox, our developers are on it.2 result = os.popen(f'grep -r "{grep}" * --exclude-dir=internal_secrets 2>/dev/null').read()
Upon investigation we learn that GNU grep supports --exclude-dir. BusyBox grep does not. On BusyBox, the flag --exclude-dir=internal_secrets is either silently ignored or treated as an error, meaning the recursive search will crawl into internal_secrets/ which is the very directory that's supposed to be protected.
In our testbed we learn that this command fails silently (2>/dev/null) so it is treated as an error. We need to be able to disable the --exclude-dir option. Fortunately, the "--" indicates "end of options" to grep in both GNU and BusyBox. The "--" can also bypass the limited input validation.
The "*" now expands inside VAULT_FOLDER, which contains our "--"file. Alphabetically "--" sorts first, so the command becomes:
1grep -r "." -- extracted internal_secrets user_secrets.txt z --exclude-dir=internal_secrets
The "." grep parameter means "match any character in regex and so it returns everything, including our flag.
Exploitation
1filenames = z -r vaults/--2action = search3grep = .4content = test
Risk
All three vulnerabilities chain together into a single, unauthenticated, one-shot exploit that completely bypasses all security controls and leaks protected secrets. No authentication, no rate limiting, no brute force needed. The Secret Manager application contains critical vulnerabilities that allow any unauthenticated attacker to fully read and overwrite all protected secret, including those explicitly marked as internal, with a single HTTP request requiring no special tools or expertise.
Remediation
Input validation is one of the most important defences against attacks from untrusted input. Comprehensive input validation protects software from a wide range of attack categories. When combined with output encoding in a web application, input validation can provide a significant barrier to attacks. Instead of checking for .. and /, resolve the full path and verify it stays within the intended directory. For example:
1for filename in filenames:2 resolved = os.path.realpath(os.path.join(UPLOAD_FOLDER, filename))3 if not resolved.startswith(UPLOAD_FOLDER + '/'):4 message = "Error: Invalid filename"5 error = True6 break
Next, reconsider invoking system commands directly from code using input from an untrusted zone. This is a significant risk for remote command injection as we see in the ability to manipulate existing code based on our input. Never use globs (*) with user controlled filenames. Instead, consider:
1import shutil23for item in os.listdir(UPLOAD_FOLDER):4 src = os.path.join(UPLOAD_FOLDER, item)5 dst = os.path.join(VAULT_FOLDER, item)6 if os.path.isfile(src): # explicitly skip directories7 shutil.copy2(src, dst)
Also consider replacing grep shell command with a python equivalent.
1import re23results_list = []4for root, dirs, files in os.walk(VAULT_FOLDER):5 # Explicitly skip internal_secrets in Python, not via shell flag6 dirs[:] = [d for d in dirs if d != 'internal_secrets']7 for file in files:8 file_path = os.path.join(root, file)9 try:10 with open(file_path, 'r') as f:11 for line in f:12 if re.search(grep, line):13 results_list.append(f"{file_path}: {line.strip()}")14 except:15 pass16result = '\n'.join(results_list)
In general, user input should never reach a shell command.



