Had this on a recent engagement and thought I'd provide a cut-down version as a fun little CTF-like challenge.

As an attacker, you can invoke `pwnme()` and control the value of `$filename` via a web request.

You cannot control the contents of the file system that this code is running on. You don't have the ability to upload files.

How do you achieve command injection?

#php #challenge

@oj Pollute and execute access logs.
@jeremy How does that achieve code exec here?
@oj You control the values entered into the access logs, you control the filename, you execute the access log filename effectively as an include.
@jeremy shell_exec doesn't include any files, and file_exists doesn't interpret the file either. Am I missing something?

@oj It's been awhile since I've done this. So it's quite possible that I'm missing something.

file_exists merely verifies the presence of the file.

shell_exec should execute any valid PHP within the file. Enter <?php opening block, functions, anything else you want to include in the access logs, escape and close the PHP block, then arbitrarily attempt to execute the block of code using the pwnme() function to execute command injection, establish a reverse shell, or nearly anything else you like. It's admittedly a sloppy approach.

However, also, like I said, it's been awhile, so my memory might be a bit foggy.

@jeremy shell_exec() is basically like system(). It runs a command on the underlying OS, it doesn't include or interpret any files like include() or require().
@oj LOL... derp.
@jeremy We've all been there :)
@oj So, if you can determine which OS is being run from server banners, and assuming that your inputs are not sanitized and no WAF is in place, then why wouldn't you simply execute a system-level reverse shell?
@jeremy I'm not interested in what commands you might run.. I want to know how you would get command exec in the first place.

@oj
Ahh, you're gonna make me dust it off. LOL... OK.

1. Attempt to determine OS from banners.
2. Determine valid path and most-likely binary available in target OS to establish command exec. Or host a binary matching your target OS on the attacking machine.
3. Select and execute the chosen binary with no parameters/args.

Remote Code Execution would probably be preferable, to grant you further control over the target binary.

Does this answer your question?

@jeremy No. It still doesn't say how you are going to execute anything.
@oj It seems like this was answered. file_exists tests for the existence of the file. shell_exec, then executes that file at the system level. If you are allowed to control the target binary by remotely hosting it, then you can craft anything you desire, while requiring no arguments.
@jeremy Nope, still not. You can't upload files (as per the question). shell_exec() is invoked with "rm {$filename}", not directly with just what you give it. You haven't indicated what string you would pass in to get past file_exists() that also works as a way to execute commands.
GitHub - justinsteven/php-file-exists-rm-challenge

Contribute to justinsteven/php-file-exists-rm-challenge development by creating an account on GitHub.

GitHub
@justinsteven @oj @jeremy I've dropped a working solution below but there are other ways to go about it, it's a fun one. Gotta love PHP!

@oj

I was thinking something like:

% wget 'http://127.0.0.1/[insert filename here]'

...but when you said above that you weren't interested in what commands I might run, I decided not to demonstrate syntax.

In the meantime, someone else already posted similar solutions.

@jeremy but you can't invoke wget.

@jeremy @oj sorry, but no.

You've suggested "poison the access logs and execute them" despite the fact that the challenge uses a shell to `rm` the filename you give it; it doesn't execute it. You've also suggested "simply execute a system-level reverse shell" by "determining most-likely binary available in target OS to establish command exec" but you haven't shown _how_ to get command exec.

Now you're saying to wget something off of 127.0.0.1 without saying if you're doing that locally on your machine, or if you're doing it on the victim's machine - in which case you're nowhere near being able to execute arbitrary commands on their machine yet, and if you could why would you want to wget their localhost anyway?

You don't then get to say "in the meantime someone else already posted similar solutions" like you were on the right track. Sorry, but you just weren't.

The challenge was "how do you achieve command injection?" not "what would you do with command injection?"

The trick used in the solutions we know work was that file_exists() will honour a URL such as ftp:// to check the existence of a file, at which point you have various places inside a ftp:// URL to hide a command injection trigger (I originally used the username/password part, and bitquark used the fragment which I think is super clever)

Saying "nice trick" or "interesting, TIL" or "yeah gotcha that makes sense" would parse. Saying nothing at all would be appropriate. Saying "that's what I was saying all along!" which is how what you're saying sounds to me (but it might just be me) just doesn't make sense. Take the L.