Facebook CTF 2019 Writeup: rceservice – Bypassing preg_match

Problem Description

We created this web interface to run commands on our servers, but since we haven't figured out how to secure it yet we only let you run 'ls'

http://challenges.fbctf.com:8085

(This problem does not require any brute force or scanning.
We will ban your team if we detect brute force or scanning).

Application Overview

The webpage has a single form that accepts a command in a JSON format

{ "cmd": "command to execute" }

Index page

As described by the problem we should be able to execute ls with:

{ "cmd": "ls" }

Sending ls gives: Sending ls

Problem

However when trying ls .. the command is rejected by the server Sending ls ..

As it turns out the request is filtered by this insane looking regex:

} elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
    echo 'Hacking attempt detected<br/><br/>';
}

As you can see, pretty much all characters except lowercase alphabet and space are not allowed.

Bypassing preg_match

One of the most common ways to bypass pregmatch is to use multiline inputs, because pregmatch only tries to match the first line. Lucky for us JSON can be formatted to be multiline, for example:

{
  "cmd": "ls /home/rceservice"
}

Since there were no checks to filter out multiline inputs, we can send this exact input to the server and recieve a welcoming response: Bypassing using multiline

Looks like there is a flag file. However, running this payload yields an empty response.

{
  "cmd": "cat /home/rceservice/flag"
}

Empty

"Escaping" the jail

As it turns out, the application's PATH variable was changed by:

putenv('PATH=/home/rceservice/jail');

This means that our current PATH contained the ls binary, which let us execute it, but not cat. We can bypass this by using the absolute path to cat which is /bin/cat. The payload then becomes:

{
  "cmd": "/bin/cat /home/rceservice/flag"
}

Which gives us the flag: Flag

Flag: fb{pr3g_M@tcH_m@K3s_m3_w@Nt_t0_cry!!1!!1!}

Final Notes

As it turns out, the multi-line JSON exploit wasn't the intended solution by the Facebook team. The intended solution involves exploiting the backtrack and recursion limit of pcre. If this limit is reached, preg_match may return the wrong matching result.