# Remote Code Execution RCE - критическая уязвимость, позволяющая удалённо исполнять bash команды из shell'а сервера. Есть 4 таска: * Инъекция с выводом результата * Байпас фильтра пробела * Блайнд * Полу-блайнд Нам был дан исходный код: ![](https://i.imgur.com/cE0viiP.png) # Инъекция с выводом результата Сразу видим, что есть GET параметр `curl`, передающийся в функцию `curl()`. Далее этот параметр именуется как $target в скопе функции. Переменная $target конкатенируется со строкой `'curl -vvv -L -sD - -o /dev/null '`. После этого на сервере вызывается linux'овая команда через функцию shell_exec(). Посколько входные данные клиента передаются "как есть" без валидации, значит мы можем подставлять что угодно и это нам позволяет модифицировать цепочку команд. ![](https://i.imgur.com/F3PAL4p.png) Чтобы добавить свою команду, нужно указать конец предыдущей. Для этого нужно передать `&&` или, допустим `;`. Куча вариантов, можно посмотреть в гугле. После этого у нас получается команда `'curl -vvv -L -sD - -o /dev/null ;'` Удостоверимся, что есть RCE. Введём `id`, чтобы посмотреть под каким юзером сидит сервер. Текущий пейлоад: `; id` Результат: ![](https://i.imgur.com/Suw6JI1.png) Отлично! Наша команда исполнилась. Посмотрим файловую структуру в текущей деректории. Пейлоад: `; ls` ![](https://i.imgur.com/Qq149yz.png) И вот у нас список файлов, в т.ч и те php, которые мы видим, когда заходим на вебчик. Тут же и видим наш заветный `flag.txt`. Логично предположить, что здесь и лежит флаг. Прочитаем файл. Пейлоад: `; cat flag.txt` ![](https://i.imgur.com/ePp5uWI.png) Вот и наш флаг. Таск решён. # Байпас фильтра пробела Казалось бы, что всё хорошо, но мы уже не сможем использовать пейлоад `; cat flag.txt`, потому что пробелы не пропускает. ![](https://i.imgur.com/vatoCwN.png) Используем переменную окружения `IFS` из линукса. > IFS. Эта переменная задает разделители полей (Internal Field Separator), которые используются при операции разделения слов при преобразованиях командной строки, выполняемых оболочкой перед тем, как запустить командную строку на исполнение. Значение этой переменной по умолчанию - "‹Пробел›‹Tab›‹Символ_новой_строки›". При создании цепочки команд в шелле, можно как раз использовать переменные окружения и делается это так: `echo $SOME_VARIABLE` У нас уже был пейлоад для чтения флага из файла. Вместо пробелов осталось подставить переменную окружения `IFS` Пейлоад: `;cat$IFS"flag.txt"` В шелл передастся `;cat "flag.txt"` Посмотрим результат. ![](https://i.imgur.com/4SXNVeN.png) # Блайнд Блайн-инъекции - это такие инъекции, при которых нет никакого вывода, но убедиться, что код исполнился можно, к примеру, инициированием зависания сервера. Если отправили пейлоад с зависанием сервера и сервер завис, можно утверждать, что присутствует блайнд-инъекция. У нас как раз такой случай. Что бы мы не отправили - ответ один и невозможно понять, исполнился ли код. ![](https://i.imgur.com/vhZD85M.png) Начинаем сверять тайминги ответа сервера. Что будет, если передадим `; id`: ![](https://i.imgur.com/s7pEUxy.png) Ответ прилетел через 42 мс. А теперь иниициируем зависание на 5 секунд с пейлоадом `; sleep 5`: ![](https://i.imgur.com/0WQ5Bf4.png) Сервер действительно завис на 5 секунд. Значит можно делать блайнды. Что-то проверять тоже можно слипами. Допустим, если какой-то файл существует, то слипаем, а если нет, то ничего не делаем. Об этом подробнее на другом вулнбоксе расскажу подробнее. # Полу-блайнд Поскольку мы удостоверились, что инъекции действительно проходят, пора через полу-блайнд инъекцию получать ответ команды , которую мы передаём. Полу-блайнд инъекция - это инъекция, при которой само приложение не возвращает никакого ответа, чтобы мы могли понять, что сервер сделал, но можем заставить направить вывод к нам. Есть куча способов полу-блайнд инъекций, но я покажу простые и частоиспользуемые. 1. HTTP Request Inspector Ресурсы: https://webhook.site https://requestinspector.com/ На примере первого ресурса рассмотрим пример. Интерфейс:![](https://i.imgur.com/Qf19VX2.png) Мы получили уникальную ссылку, перейдя но которую, наш интерсептер покажет весь HTTP запрос. Мы можем сюда передавать raw body, заголовки, GET/POST параметры и т.д. Я люблю всё писать в raw data, потому что туда можно передать много данных и они будут читабельными. Нам нужно прочитать файл файл flag.txt и передать в raw data на наш интерсептор. Для передачи данных с сервера на мой интерсептор я буду использовать curl, потому что мы видим его работу прямо в приложении. Билдим пейлоад: ![](https://i.imgur.com/E5R26Hl.png) В --data я передал результат команды `cat flag.txt`. Да, чтобы получить результат команды, можно сделать $(cat flag.txt), а можно \`cat flag.txt\`. Как хотите. Всё зависит от фильтров в приложении и вашего желания. Так же, можно отправлять stderr(поток ошибок), stdout(стандартный поток). stderr - это, допустим, сообщение об ошибке, что команда не найдена. stdout - всё, что мы видим в консоли, когда отправляем команду на установку программы, к примеру. Итак, отправляем пейлоад на сервер и в интерсепторе открываем полученный запрос. ![](https://i.imgur.com/Izduv7L.png) Флаг лежит в raw body, как мы и хотели. 2. Socket Server Можно сделать свой интерсептор, подняв у себя сокет-сервер, о котором мы разговаривали на прошлых тренировках. На линуксе сокет поднимается просто: `nc -nvlp любой_незанятый_порт`. Я подниму его у себя на 7777 порте. Важное условие: чтобы 7777 порт был доступен всем в интернете, он должен быть прокинут во внешку. Когда вы изначально открываете сокет на своём порте, то к нему могут подключиться могут ТОЛЬКО из Вашей сети. К примеру, если все клиенты подключены к одной WIFI-сети. Открывать порты - отдельная тема для разговора. Нужен "белый" IP адрес, настройка маршрутизатора, которая пробросит локальный порт во внешку и т.д. Есть простое и быстрое решение проблемы - туннелирование. Поможет нам в этом ngrok. Вы открываете у себя порт, запускаете ngrok с этим же портом и создаётся туннель из локальной сети в интернет. Чтобы получить доступ к этому порту, достаточно из интернета перейти по сгенерированной ngrok'ом ссылке. Настройка занимает от силы минуту, поэтому можете посмотреть быстрый[гайд по настройке ngrok'а](https://www.youtube.com/watch?v=ti6NpBHxYl0). Вернёмся к сокет-серверу. Поскольку я разворачиваю его на VDS, у меня белый IP и все порты прокидываются во внешку сразу. Мне достаточно написать `nc -nvlp 7777` и из интернета любой сможет подключиться ко мне безо всяких туннелей. Нас встречает такое сообщение, говорящее, что порт 7777 прослушивается и ждёт соединения. ![](https://i.imgur.com/jeQDu8Y.png Скрафтим пейлоад с тем же HTTP запросом на наш самодельный интерсептор, но теперь ссылку нужно поменять на IP адрес нашего сокет-сервера и его порт: ![](https://i.imgur.com/38tR4Yr.png) Поскольку HTTP запрос - текст и общение между клиентом и сервером осуществляется через сокеты, нам ничего не помешало отправить HTTP запрос на наш сокет-сервер. В теле запроса мы получили тот же флаг. ![](https://i.imgur.com/sapxSvi.png) Мы на реальном примере мы убедились, что нельзя допускать появления RCE уязвимостей. Наш сервер начали закидывать форк-бомбами и тачка легла. Решением этой проблемы стало ограничением выделенных ресурсов под таск. Чтобы не допускать RCE, старайтесь не исполнять shell-скрипты на веб-сервере. А если этого не избежать, то регулярками проверяйте, что прилетел действительно тот формат данных, который ожидался. В нашем случае нужно было проверять, что клиент прислал URL и ничего больше. Несколько важных умозаключений: 1. Никогда не доверяй клиенту. Что бы ты не хотел ожидать от него, прилететь может мусор, эксплуатирующий один или цепочку уязвимостей. Всегда нужна валидация входных данных. 2. Защита от RCE "фильтровать ;" или что-то подобное - не решение. Есть куча байпасов и их не пересчитать. Старайтесь пропускать либо число, либо A-z0-9_, тогда проблем не будет. 3. Думайте о защите на этапе проектирования веб-сервиса, а не когда тачка лежит и её уже не спасти. 4. Закрепление хакером на машине через RCE может быть настолько серьёзным, что может просто не получиться отобрать доступ. Всем спасибо! ©LIFE TEAM, madwayz