Pwnable.kr cmd1 & cmd2 Writeups

Chris Long
5 min readNov 26, 2017

With the Thanksgiving holidays providing a bit of free time, I decided to poke at some of the challenges on http://pwnable.kr. I’ve been working through a handful of them over the last few days, but I really enjoyed the cmd1 and cmd2 challenges.

cmd1

The object of these challenges is usually to view the contents of the “flag” file, often by finding flaws in a compiled C binary. Here was cmd1.c:

The premise of this challenge is simple. Provide a command in the form of a command line argument and the program will pass the argument to the system() function to be executed for us.

Normally, the following command would do the trick:

$ ./cmd1 “cat ./flag”

However, there’s a few things to note about this challenge:

  • It’s stomping all over the PATH environment variable. We need to determine how that will affect us when trying to solve this challenge.
  • There is a function called “filter”, that upon examination, is designed to prevent us from using certain strings in our command.

Now that we know what our limitations are, we can start to figure out how to work around them.

Without going into too much detail, the PATH environment variable is what tells the shell which directories executable files are located in. To view the current list of directories in your PATH, you could type “env | grep PATH”. The reason your shell knows where grep is (/bin/grep) is because the /bin folder is included in that variable. By setting the PATH variable to “/fuckyouverymuch”, this program essentially makes our shell “blind” and unable to automatically search for executables.

Luckily, the workaround for this is quite simple. If we want to use a command like “cat”, we just have to figure out where it is and specify the full path. You can find the location by using the “which” command.

$ which cat
/bin/cat

The filter is slightly more tricky. We won’t be able to use the strings “flag”, “sh”, or “tmp” in our command. If that’s the case, how will we be able to get the contents of the flag file?

I immediately began thinking about obfuscation techniques and wondered how I could get the shell to accept a string that would evaluate to “flag” without typing it directly. Luckily I didn’t have to think too hard. It turns out, this works:

$ echo “f””l””a””g”
flag

With both of these workarounds under our belt, we have all the information we need to pass this challenge. Instead of simply using “cat”, we’ll specify the full path to the binary since the PATH variable has been clobbered. Also, instead of using “flag” which will get caught by the filter, we can use “f””l””a””g”. However, we have to be careful to escape the quotes correctly!

$ ./cmd1 “/bin/cat \”f\”\”l\”\”a\”\”g\””
mommy now I get what PATH environment is for :)

cmd2

Now that we’ve passed cmd1 and the mechanics are fresh in our mind, let’s check out cmd2.

The basics of the program are the same. PATH gets clobbered, but the filter is WAY more strict. Not having the ability to specify a forward slash is going to make specifying the full path to variables quite tricky. However, I know from past experience that shells have built-in commands that don’t need to refer to other executables on the file system. I started with the basic failure case:

$ ./cmd2 cat ./flag
cat
sh: 1: cat: not found

Just as we figured. Our shell doesn’t know where cat is, and because slashes will get blocked by the filter, we’ll need to find a way around this problem. Because the command is executed via system(), we can see that it will use the default sh shell to execute the final command.

A quick look at the man page for sh is a good start here. The following section confirms my thought about using shell built-ins:

Path Search
When locating a command, the shell first looks to see if it has a shell function by that name. Then it looks for a builtin command by that name. If a builtin command is not found, one of two things happen:
1. Command names containing a slash are simply executed without performing any searches.2. The shell searches each entry in PATH in turn for the command. The value of the PATH variable should be a series of entries separated by colons. Each entry consists of a directory name. The current directory may be
indicated implicitly by an empty directory name, or explicitly by a single period.

By pure chance, I looked up a list of shell builtins and came across this page: http://manpages.ubuntu.com/manpages/xenial/man7/bash-builtins.7.html

Within a few minutes, I came across this section that immediately piqued my interest:

command [-pVv] command [arg ...]
Run command with args suppressing the normal shell function
lookup. Only builtin commands or commands found in the PATH are
executed. If the -p option is given, the search for command is
performed using a default value for PATH that is guaranteed to
find all of the standard utilities.
If either the -V or -v
option is supplied, a description of command is printed. The -v
option causes a single word indicating the command or file name
used to invoke command to be displayed; the -V option produces a
more verbose description. If the -V or -v option is supplied,
the exit status is 0 if command was found, and 1 if not. If
neither option is supplied and an error occurred or command
cannot be found, the exit status is 127. Otherwise, the exit
status of the command builtin is the exit status of command.

Interesting! Using the shell built-in called “command” supplied with the -p flag allows us to supply a default value of PATH. I had never used this built-in before, but it sounded promising. Let’s test it out:

$ ./cmd2 “command -p cat doesntexist”
command -p cat doesntexist
cat: doesntexist: No such file or directory

Awesome! Our shell seems to know where cat is now, even though we haven’t specified the full path to it. After a quick look at the filter, we observe that double quotes aren’t filtered either, so maybe we can reuse our “obfuscation” technique from cmd1!

$ ./cmd2 “command -p cat \”f\”\”l\”\”a\”\”g\””
command -p cat “f””l””a””g”
FuN_w1th_5h3ll_v4riabl3s_haha

I had a lot of fun solving these two challenges, and I’m betting there are dozens of ways to solve each one. I learned a bit more about PATH and shell commands, so I consider it time well spent :)

--

--