Dojo challenge #30 winners!

March 12, 2024

The 30th Dojo Challenge, 'Terminal Isolation', invited participants to exploit a command injection vulnerability and gain remote code execution on the target system.

We are delighted to announce the winners of Dojo Challenge #30 below.

💡 Want to create your own monthly Dojo challenge? Send us a message on Twitter!


Congrats to aendxr, pwnwithlove and truff for the best write-ups 🥳

The swag is on its way! 🎁

Subscribe to our Twitter and/or LinkedIn feeds to be notified of upcoming challenges.

Read on to find out how one of the winners managed to solve the challenge.

The challenge

We asked you to produce a qualified report explaining the logic allowing exploitation, as set out by the Terminal Isolation challenge.

This write-up serves two purposes:

  • To ensure contestants actually solved the challenge themselves rather than copy-pasting the answer from elsewhere.
  • To determine contestants' ability to properly describe a vulnerability and its vectors within a professionally redacted report. This gives us invaluable hints on your unique talent as a bug hunter.


We want to thank everyone who participated and reported to the Dojo challenge. Many other high quality reports were submitted alongside those of the three winners. 😉

Below is the best write-up overall. Thanks again for all your submissions and thanks for playing!

aendxr‘s Write-Up

————– START OF aendxr‘s REPORT —————


OS Command Injection is a critical security vulnerability which allows attackers to execute arbitrary commands on the host operating system of an application. This type of vulnerability arises when user-supplied input is improperly validated or sanitized before being passed to system commands for execution. In such scenarios, attackers can manipulate the input to inject malicious operating system commands, which are then executed with the privileges of the application.

In the context of this application, the OS command injection vulnerability is introduced within the run() function of the Terminal class. This function processes user-provided input with inadequate validation and sanitization, leading to the execution of arbitrary commands in the server environment.


The mechanisms employed by the application to mitigate against OS command injection vulnerabilities are insufficient, leading to Remote Code Execution (RCE) in an unauthenticated context, as outlined below.

Initial input validation

$arry = preg_split('/[\s]+/', urldecode(""));

$result = "";
if ( count($arry) !== 2 ) {
    $result = "\nError: only one command along with one argument is allowed";
} else {
    $result = $terminal->run($arry[0], $arry[1]);

The PHP code snippet above first URL decodes the input using urldecode() and splits it into multiple array elements using any whitespace characters as delimiters via preg_split('/[\s]+/'.

Next, the application checks if the resulting array contains exactly two elements, which correspond to one command and one argument. If the count is not equal to 2, an error message is generated and the command execution is aborted. However, this approach is not foolproof against command injection, which can occur even if spaces do not separate multiple arguments.

For example, the input payload echo $(cat<>/tmp/flag.txt) uses shell command substitution $(...) to execute the malicious cat command without relying on spaces.

The mechanism behind command substitution will be explored in more detail later. What is important to highlight for now is that the payload uses shell redirection operators <> to read the contents of the file /tmp/flag.txt. Therefore, it demonstrates how complex commands can be constructed without spaces to bypass simplistic input validation checks.

Input sanitization using str_replace()

// Quotes or brackets should never be used no matter what
$arg = str_replace(['"', "'", '`', '{', '}'], '_', $arg);

In addition to the previous input validation, the application performs sanitization against the argument provided by replacing certain dangerous characters with underscores (_): double quotes ("), single quotes ('), backticks (`) and curly braces ({}). However, the set of dangerous characters implemented is not comprehensive by any means and therefore leads to incomplete sanitization.

Using the payload referenced above again as an example, the argument $(cat<>/tmp/flag.txt) does not rely on any of the aforementioned dangerous characters and instead uses special characters associated with command substitution $() and input redirection <>.

Input sanitization using escapeshellarg()

// Escape the argument for the command
$arg = escapeshellarg($arg);
$c = sprintf('echo -n "%s"', $arg);
return "$cmd $arg\n". shell_exec($c);

The built-in PHP function escapeshellarg() is used to perform further sanitization. This function adds single quotes around the argument and escapes any single quotes within the string by adding a backslash before them. However, it is important to note that escapeshellarg() may not neutralize all potential injection vectors, even when combined with previous validation and sanitization techniques.

The overall security of the application also depends on how the argument is used subsequently. In this case, using the echo command to handle user-supplied input poses a risk, as echo can be manipulated to execute arbitrary commands or output data in unintended ways.

Command substitution within echo argument

The construction of the echo command using sprintf('echo -n "%s"', $arg) in particular introduces an important vulnerability. Despite the escapeshellarg() function's effort to secure the argument by enclosing it within single quotes, by placing its output within double quotes as in "%s"', $arg, the command passed to shell_exec() for execution allows for shell command substitution.

Given the argument $(cat<>/tmp/flag.txt) provided by the payload, the final command launched by shell_exec() will be the following.

echo -n "'$(cat<>/tmp/flag.txt)'"

There is a mix of single and double quotes in echo -n " '$(...)' " making things confusing. The '$()' part is where the shell "command substitution" takes place. This means that whatever command is placed inside $() gets executed independently, and its output replaces $() in the string.

If the echo argument didn't contain the final enclosing double quotes, as in the command below, this would prevent command substitution due to the way it is interpreted by the shell.

echo -n '$(cat<>/tmp/flag.txt)'

However, since the shell treats everything within double quotes as an expression to be evaluated, including a string already enclosed within single quotes, this allows for command substitution to occur. In this context, it allows the shell to interpret the payload as a command to read from the file /tmp/flag.txt.

This oversight demonstrates a crucial point in secure coding practices: even when arguments are properly escaped, the context in which they are used can introduce vulnerabilities. Thus, ensuring that commands constructed with user-supplied input are handled securely requires careful consideration of both the escaping mechanism and the way in which the input is subsequently processed or executed.


Providing the input payload echo $(cat<>/tmp/flag.txt) to the endpoint results in the disclosure of the /tmp/flag.txt file content: 'FLAG{Acc3ss_Gr4nt3d}'.



Exploitation of the vulnerability can lead to unauthorized access to sensitive data stored on the server, potentially compromising user information, including personally identifiable information (PII) and other confidential resources.


Attackers could manipulate or tamper with sensitive data stored on the server by executing arbitrary commands, leading to unauthorized modifications and compromising data integrity.


A successful attack could result in service disruptions or downtime, preventing legitimate users from accessing the application or its services, thereby affecting availability.


As an interim measure, the following patch should be applied to the existing sanitization process in order to increase its robustness.

$arg = str_replace(['"', "'", '`', '{', '}', '<', '>', '$', '(', ')'], '_', $arg);

Additionally, the echo command construction should be modified by removing the enclosing double quotes, as below.

$c = sprintf('echo -n %s', $arg);

Neither of the previous hotfixes should introduce any unintended side-effects.

To effectively mitigate against further OS command injection vectors and enhance the overall security of the application, more robust mechanisms should be implemented. It is always preferable to rely on standardized, built-in PHP functions designed for security and input handling. escapeshellcmd() should be used in conjunction with escapeshellarg() to sanitize both the command and its argument. escapeshellcmd() ensures that any shell metacharacters in the command itself are escaped, preventing command injection attempts at the command level.

————– END OF aendxr‘s REPORT —————