HHIDE_DUMP
Гость
H
HHIDE_DUMP
Гость
Салом КулХацкер!Админ был в отдыхе.Посты придумывал в течении всего времени.И нашел уязвимость в ЯваСкрипт и готов вам рассказать про нее Посмотреть вложение 9996
avaScript один из популярнейших языков, используемых в работе сайтов. Благодаря такой популярности, можно найти самые разные примеры применения JavaScript. Из них самыми бессмысленными являются:
Для демонстрации уязвимости JavaScript мы будем обходить защиту в
Итак, ставим в DVWA уровень безопасности low (делается во вкладке DVWA Security) и переходим на страницу «JavaScript Attacks» и видим там следующее:
В качестве задания нам нужно передать слово «success» через форму на сайте. Пробуем: вписываем «success» и отправляем. Получаем ошибку «Invalid token.»:
Открываем исходный код страницы. Если у вас с этим проблемы на целевом сайте в реальной ситуации, то смотрите «
Между тегами <script></script> там следующее:
Я могу понять только следующие строки:
function generate_token() {
var phrase = document.getElementById("phrase").value;
document.getElementById("token").value = md5(rot13(phrase));
}
generate_token();
Также посмотрим на форму, через которое мы должны отправить слово «success»:
<form name="low_js" method="post">
<input type="hidden" name="token" value="" id="token" />
<label for="phrase">Phrase</label> <input type="text" name="phrase" value="ChangeMe" id="phrase" />
<input type="submit" id="send" name="send" value="Submit" />
</form><script>
В функции generate_token() в первой строке переменной phrase присваивается то значение, которое имеет поле с идентификатором (id) phrase в форме. Затем во второй строке рассматриваемой функции значение переменной phrase обрабатывается двумя функциями и их значение присваивается элементу token в форме.
За пределами функция имеется код вызова этой функции:
generate_token();
Данный вызов не привязан ни к какому событию (например, отправка формы) или условию. Это означает, что когда страница открыта в нашем браузере, функция generate_token() уже выполнена, то есть значение токена для строки «ChangeMe» уже вычислено и присвоено полю ввода с идентификатором token. По этой причине, когда мы меняем значение поля ввода phrase, оно не совпадает с токеном о чём мы и получаем сообщение.
Получается, единственный способ справится с заданием — это изменить значение поля phrase с «ChangeMe» на «success» до открытия в веб браузере. Как это можно сделать?
Это можно сделать в
Но я покажу вам совсем «детский» способ, который я использовал с первых лет появления у меня компьютера, для этого способа вообще не нужны никакие инструменты.
Суть элементарна: сохраняем страницу себе на компьютер, открываем редактором (любым текстовым или для HTML кода), делаем нужные правки, открываем эту страницу в браузере и отправляем!
Сохраняем (имя я выбрал покороче, иначе из-за специальных символов могут быть проблемы):
Открываем файл *.html и находим форму:
Как вы можете увидеть — она изменилась, а именно присвоено значение полю token — видимо, это сделал браузер при сохранении:
<form name="low_js" method="post">
<input type="hidden" name="token" value="8b479aefbd90795395b3e7089ae0dc09" id="token">
<label for="phrase">Phrase</label> <input type="text" name="phrase" value="ChangeMe" id="phrase">
<input type="submit" id="send" name="send" value="Submit">
</form>
Итак, во-первых, меняем «ChangeMe» на «success».
Во-вторых, у этой формы отсутствует атрибут action. В атрибуте action прописывается страница, куда отправляются данные. Если атрибут отсутствует (это допускается), то данные отправляются на тот же самый адрес, по которому размещена страница с формой. Это работало нормально, когда у страницы был адрес
Иногда у страницы может быть уже установлен action, но адрес указан как относительный путь — в этом случае опять же отправка формы не будет работать как нам нужно. Для исправления просто укажите абсолютный путь до страницы на целевом сайте.
В-третьих, как я упомянул, при сохранении страницы браузер вписал значение token — уберём его (хотя, по логике, это необязательно, так как новое значение будет рассчитано и присвоено после открытия страницы).
Итак, у меня получилась следующая форма:
<form name="low_js" method="post" action="
<input type="hidden" name="token" value="" id="token">
<label for="phrase">Phrase</label> <input type="text" name="phrase" value="success" id="phrase">
<input type="submit" id="send" name="send" value="Submit">
</form>
Сохраняю её и открываю файл в браузере:
Нажимаю кнопку «Submit»:
Обратите внимание на адрес страницы — мы вновь на сервере. При этом мы получили сообщение «Well done!» - то есть задача выполнена. Таким простым способом мы обошли защиту JavaScript и нам даже не пришлось разбираться в сложных алгоритмах расчёта маркера — достаточно было сделать небольшое изменение в реперной точке.
2. Изменение переменных JavaScript во время отладки
В веб-браузере откройте Инструменты разработчика (клавиша F12), перейдите во вкладку «Sources», выберите нужный файл (в нашем случае это (index)) и найдите в коде строку, где мы хотим сделать прерывание:
Кликните на номер строки для установки точки прерывания:
Обратите внимание, что в этой строке мы можем установить до четырёх прерываний, поскольку выполняется четыре операции. Я ставлю точку прерывания в самую правую часть — на первое действие:
Значение токена уже установлено, поэтому перезагрузим страницу, чтобы все скрипты выполнились заново.
Видим, что выполнение замерло на точке прерывания:
Присмотримся к этой информации:
Поменяем в этом окне значение phrase и нажмём кнопку «Resume script execution» (можно нажать F8):
На странице будут выполнены оставшиеся скрипты. Можно убрать точку прерывания и даже закрыть Инструменты разработчика.
Что мы имеем? Значение токена рассчитано для строки «success», теперь нам достаточно вписать эту строку в поле формы:
И отправить на сервер:
3. Отладка JavaScript во внешних файлах
В DVWA поднимите уровень безопасности до medium.
Между прочим, даже до вникания в особенности работы страницы, обратите внимание, что «детский» метод с сохранением страницы и изменением формы по-прежнему работает!
Но приступим к отладке, поскольку далеко не все случаи такие простые: например, форма отправки может вообще отсутствовать в исходном коде и создаваться на лету средствами JavaScript.
Посмотрим в исходный код страницы, JavaScript был вынесен в отдельный файл:
<script src="../../vulnerabilities/javascript/source/medium.js"></script>
Содержимое этого файла такое:
function do_something(e){for(var t="",n=e.length-1;n>=0;n--)t+=e[n];return t}setTimeout(function(){do_elsesomething("XX")},300);function do_elsesomething(e){document.getElementById("token").value=do_something(e+document.getElementById("phrase").value+"XX")}
Чтобы сделать код JavaScript читаемым обратитесь к статье «
Скачиваем интересующий файл:
wget
Запускаем деобфускацию:
./jstillery_cli.js medium.js
Получено:
function do_something(e)
/*Scope Closed:true*/
{
for (var t = '', n = e.length - 1; n >= 0; n--)
t += e[n];
return t;
}
setTimeout(function ()
/* Called:undefined | Scope Closed:false| writes:false*/
{
do_elsesomething('XX');
}, 300);
function do_elsesomething(e)
/*Scope Closed:false | writes:true*/
{
document.getElementById('token').value = do_something(e + document.getElementById('phrase').value + 'XX');
}
Если присмотреться к исходной строке, то можно увидеть, что код не обфусцирован, а сжат. В любом случае, теперь стало намного понятнее.
Из трёх функций фактически используется только последняя, а остальные, видимо, добавлены чтобы нас запутать.
Начинаем отладку кода, чтобы заменить значение переменной для получения нужного нам токена.
В веб-браузере вновь переходим в Инструменты разработчика (F12), находим файл medium.js. Чтобы привести сжатый JavaScript в понятный вид нажмите кнопку { }:
Анализ кода подсказывает, что стоит обратить внимание на функцию:
function do_elsesomething(e) {
document.getElementById("token").value = do_something(e + document.getElementById("phrase").value + "XX")
}
Ставим точку прерывания там. Поскольку в одной строке выполняется сразу несколько операций, выбираем последнюю позицию для точки прерывания:
Перезагружаем страницу.
Видим, что переменной e присвоено значение «XX». Нажимаем F9 для перехода к следующему шагу.
Как видно, я был неправ говоря, что первые две функции добавлены только чтобы нас запутать — готовится вызов первой функции и в качестве параметра e ей будет передано «XXChangeMeXX». Видимо, это и есть начало вычисления токена.
Я присвоил параметру e значение «success», но получил ошибку о неверном токене. По аналогии с передаваемым значением («XXChangeMeXX»), назначем e строку «XXsuccessXX».
Затем нажимаем F8 для продолжения выполнения кода JavaScript без отладки и прерываний.
Вводим на странице слово «success»:
И отправляем:
Как можно увидеть, всё прошло сработало.
4. Отладка обфусцированного кода JavaScript
В DVWA поднимите уровень безопасности до High.
JavaScript код вновь вынесен в файл high.js, посмотрим на него:
Код обфусцирован, я попробовал применить
Возвращаемся к статье «
После деобфускации получился большой фрагмент JavaScript кода.
В конце кода есть следующие функции:
function do_something(e) {
for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n];
return t
}
function token_part_3(t, y = "ZZ") {
document.getElementById("token").value = sha256(document.getElementById("token").value + y)
}
function token_part_2(e = "YY") {
document.getElementById("token").value = sha256(e + document.getElementById("token").value)
}
function token_part_1(a, b) {
document.getElementById("token").value = do_something(document.getElementById("phrase").value)
}
document.getElementById("phrase").value = "";
setTimeout(function() {
token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);
Обратите внимание на строку:
document.getElementById("token").value = do_something(document.getElementById("phrase").value)
В ней считывается значение поле phrase. У меня подозрение, что огромный код до этих функций не выполняет ничего нужного и размещён просто чтобы запутать нас. Но в любом случае, хотим ли мы делать отладку всего кода либо только указанного фрагмента, главный вопрос — как именно в файл
Выход из этой ситуации есть — причём функция изменения файлов на лету и сохранение этих изменений даже после перезагрузки страницы есть прямо в Инструментах разработчика браузера. Подробности смотрите в статье «
Вставляем деобфусцированный код, ставим точку прерывания:
Добавляем эту страницу в «Save for overrides» как это описано в статье по ссылке выше и перезагружаем страницу.
Пожалуй, не буду разбирать этот пример до конца — будет это будет домашним заданием.
Заключение
На практике встречаются очень сложные для анализа варианты использования JavaScript, комбинирующие динамическое построение DOM структуры страницы, динамическое назначение событий, обфускацию кода, использование громоздких фреймворков и т. д. Но в целом исходить нужно из того, что защиты на стороне пользователя, в том числе с использованием JavaScript априори являются ненадёжными.
JavaScript может использоваться, например, для верификации данных в форме, но только для удобства пользователя, чтобы сообщить ему, что пропущено какое-то обязательное поле или введена информация в неверном формате. Но на стороне сервера всё обязательно должно перепроверяться так, как будто бы на стороне пользователя не было никаких проверок. Нужно исходить из того, что с пользовательской стороны может прийти что угодно.
avaScript один из популярнейших языков, используемых в работе сайтов. Благодаря такой популярности, можно найти самые разные примеры применения JavaScript. Из них самыми бессмысленными являются:
- контроль доступа с помощью JavaScript
- защита части или всего контента с помощью JavaScript
- верификация данных методами JavaScript без перепроверки на стороне сервера
Для демонстрации уязвимости JavaScript мы будем обходить защиту в
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
. Для установки DVWA себе на компьютер смотрите статьи:- Пожалуйста, Вход или Регистрация для просмотра содержимого URL-адресов!
- Пожалуйста, Вход или Регистрация для просмотра содержимого URL-адресов!
Итак, ставим в DVWA уровень безопасности low (делается во вкладке DVWA Security) и переходим на страницу «JavaScript Attacks» и видим там следующее:
В качестве задания нам нужно передать слово «success» через форму на сайте. Пробуем: вписываем «success» и отправляем. Получаем ошибку «Invalid token.»:
Открываем исходный код страницы. Если у вас с этим проблемы на целевом сайте в реальной ситуации, то смотрите «
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
». В любом случае настоятельно рекомендуется предварительно изучить «Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
».Между тегами <script></script> там следующее:
Я могу понять только следующие строки:
function generate_token() {
var phrase = document.getElementById("phrase").value;
document.getElementById("token").value = md5(rot13(phrase));
}
generate_token();
Также посмотрим на форму, через которое мы должны отправить слово «success»:
<form name="low_js" method="post">
<input type="hidden" name="token" value="" id="token" />
<label for="phrase">Phrase</label> <input type="text" name="phrase" value="ChangeMe" id="phrase" />
<input type="submit" id="send" name="send" value="Submit" />
</form><script>
В функции generate_token() в первой строке переменной phrase присваивается то значение, которое имеет поле с идентификатором (id) phrase в форме. Затем во второй строке рассматриваемой функции значение переменной phrase обрабатывается двумя функциями и их значение присваивается элементу token в форме.
За пределами функция имеется код вызова этой функции:
generate_token();
Данный вызов не привязан ни к какому событию (например, отправка формы) или условию. Это означает, что когда страница открыта в нашем браузере, функция generate_token() уже выполнена, то есть значение токена для строки «ChangeMe» уже вычислено и присвоено полю ввода с идентификатором token. По этой причине, когда мы меняем значение поля ввода phrase, оно не совпадает с токеном о чём мы и получаем сообщение.
Получается, единственный способ справится с заданием — это изменить значение поля phrase с «ChangeMe» на «success» до открытия в веб браузере. Как это можно сделать?
Это можно сделать в
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
, который среди прочего может менять содержимое любой части (заголовки и тело запросов и ответов) HTTP на лету.Но я покажу вам совсем «детский» способ, который я использовал с первых лет появления у меня компьютера, для этого способа вообще не нужны никакие инструменты.
Суть элементарна: сохраняем страницу себе на компьютер, открываем редактором (любым текстовым или для HTML кода), делаем нужные правки, открываем эту страницу в браузере и отправляем!
Сохраняем (имя я выбрал покороче, иначе из-за специальных символов могут быть проблемы):
Открываем файл *.html и находим форму:
Как вы можете увидеть — она изменилась, а именно присвоено значение полю token — видимо, это сделал браузер при сохранении:
<form name="low_js" method="post">
<input type="hidden" name="token" value="8b479aefbd90795395b3e7089ae0dc09" id="token">
<label for="phrase">Phrase</label> <input type="text" name="phrase" value="ChangeMe" id="phrase">
<input type="submit" id="send" name="send" value="Submit">
</form>
Итак, во-первых, меняем «ChangeMe» на «success».
Во-вторых, у этой формы отсутствует атрибут action. В атрибуте action прописывается страница, куда отправляются данные. Если атрибут отсутствует (это допускается), то данные отправляются на тот же самый адрес, по которому размещена страница с формой. Это работало нормально, когда у страницы был адрес
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
. Но когда я открою мой сохранённый файл, в качестве адреса у него будет что-то вроде file:///home/mial/Загрузки/1.html и браузер попытается отправить данные из формы по адресу file:///home/mial/Загрузки/1.html, а нам нужно отправить на сервер. Поэтому мы добавляем к форме атрибут action и в качестве его значения укажем адрес, куда должны будут отправится данные (в нашем случае исходный адрес формы): action="Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
"Иногда у страницы может быть уже установлен action, но адрес указан как относительный путь — в этом случае опять же отправка формы не будет работать как нам нужно. Для исправления просто укажите абсолютный путь до страницы на целевом сайте.
В-третьих, как я упомянул, при сохранении страницы браузер вписал значение token — уберём его (хотя, по логике, это необязательно, так как новое значение будет рассчитано и присвоено после открытия страницы).
Итак, у меня получилась следующая форма:
<form name="low_js" method="post" action="
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
"><input type="hidden" name="token" value="" id="token">
<label for="phrase">Phrase</label> <input type="text" name="phrase" value="success" id="phrase">
<input type="submit" id="send" name="send" value="Submit">
</form>
Сохраняю её и открываю файл в браузере:
Нажимаю кнопку «Submit»:
Обратите внимание на адрес страницы — мы вновь на сервере. При этом мы получили сообщение «Well done!» - то есть задача выполнена. Таким простым способом мы обошли защиту JavaScript и нам даже не пришлось разбираться в сложных алгоритмах расчёта маркера — достаточно было сделать небольшое изменение в реперной точке.
2. Изменение переменных JavaScript во время отладки
В веб-браузере откройте Инструменты разработчика (клавиша F12), перейдите во вкладку «Sources», выберите нужный файл (в нашем случае это (index)) и найдите в коде строку, где мы хотим сделать прерывание:
Кликните на номер строки для установки точки прерывания:
Обратите внимание, что в этой строке мы можем установить до четырёх прерываний, поскольку выполняется четыре операции. Я ставлю точку прерывания в самую правую часть — на первое действие:
Значение токена уже установлено, поэтому перезагрузим страницу, чтобы все скрипты выполнились заново.
Видим, что выполнение замерло на точке прерывания:
Присмотримся к этой информации:
Поменяем в этом окне значение phrase и нажмём кнопку «Resume script execution» (можно нажать F8):
На странице будут выполнены оставшиеся скрипты. Можно убрать точку прерывания и даже закрыть Инструменты разработчика.
Что мы имеем? Значение токена рассчитано для строки «success», теперь нам достаточно вписать эту строку в поле формы:
И отправить на сервер:
3. Отладка JavaScript во внешних файлах
В DVWA поднимите уровень безопасности до medium.
Между прочим, даже до вникания в особенности работы страницы, обратите внимание, что «детский» метод с сохранением страницы и изменением формы по-прежнему работает!
Но приступим к отладке, поскольку далеко не все случаи такие простые: например, форма отправки может вообще отсутствовать в исходном коде и создаваться на лету средствами JavaScript.
Посмотрим в исходный код страницы, JavaScript был вынесен в отдельный файл:
<script src="../../vulnerabilities/javascript/source/medium.js"></script>
Содержимое этого файла такое:
function do_something(e){for(var t="",n=e.length-1;n>=0;n--)t+=e[n];return t}setTimeout(function(){do_elsesomething("XX")},300);function do_elsesomething(e){document.getElementById("token").value=do_something(e+document.getElementById("phrase").value+"XX")}
Чтобы сделать код JavaScript читаемым обратитесь к статье «
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
». Я воспользуюсь инструментом Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
.Скачиваем интересующий файл:
wget
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
Запускаем деобфускацию:
./jstillery_cli.js medium.js
Получено:
function do_something(e)
/*Scope Closed:true*/
{
for (var t = '', n = e.length - 1; n >= 0; n--)
t += e[n];
return t;
}
setTimeout(function ()
/* Called:undefined | Scope Closed:false| writes:false*/
{
do_elsesomething('XX');
}, 300);
function do_elsesomething(e)
/*Scope Closed:false | writes:true*/
{
document.getElementById('token').value = do_something(e + document.getElementById('phrase').value + 'XX');
}
Если присмотреться к исходной строке, то можно увидеть, что код не обфусцирован, а сжат. В любом случае, теперь стало намного понятнее.
Из трёх функций фактически используется только последняя, а остальные, видимо, добавлены чтобы нас запутать.
Начинаем отладку кода, чтобы заменить значение переменной для получения нужного нам токена.
В веб-браузере вновь переходим в Инструменты разработчика (F12), находим файл medium.js. Чтобы привести сжатый JavaScript в понятный вид нажмите кнопку { }:
Анализ кода подсказывает, что стоит обратить внимание на функцию:
function do_elsesomething(e) {
document.getElementById("token").value = do_something(e + document.getElementById("phrase").value + "XX")
}
Ставим точку прерывания там. Поскольку в одной строке выполняется сразу несколько операций, выбираем последнюю позицию для точки прерывания:
Перезагружаем страницу.
Видим, что переменной e присвоено значение «XX». Нажимаем F9 для перехода к следующему шагу.
Как видно, я был неправ говоря, что первые две функции добавлены только чтобы нас запутать — готовится вызов первой функции и в качестве параметра e ей будет передано «XXChangeMeXX». Видимо, это и есть начало вычисления токена.
Я присвоил параметру e значение «success», но получил ошибку о неверном токене. По аналогии с передаваемым значением («XXChangeMeXX»), назначем e строку «XXsuccessXX».
Затем нажимаем F8 для продолжения выполнения кода JavaScript без отладки и прерываний.
Вводим на странице слово «success»:
И отправляем:
Как можно увидеть, всё прошло сработало.
4. Отладка обфусцированного кода JavaScript
В DVWA поднимите уровень безопасности до High.
JavaScript код вновь вынесен в файл high.js, посмотрим на него:
Код обфусцирован, я попробовал применить
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
, но принципиально ничего не изменилось — код остался полностью нечитаем.Возвращаемся к статье «
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
» и попробуем различные инструменты. Самый лучший результат показал JavaScript онлайн деобфускатор deobfuscatejavascript.com, адрес этого сервиса: Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
После деобфускации получился большой фрагмент JavaScript кода.
В конце кода есть следующие функции:
function do_something(e) {
for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n];
return t
}
function token_part_3(t, y = "ZZ") {
document.getElementById("token").value = sha256(document.getElementById("token").value + y)
}
function token_part_2(e = "YY") {
document.getElementById("token").value = sha256(e + document.getElementById("token").value)
}
function token_part_1(a, b) {
document.getElementById("token").value = do_something(document.getElementById("phrase").value)
}
document.getElementById("phrase").value = "";
setTimeout(function() {
token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);
Обратите внимание на строку:
document.getElementById("token").value = do_something(document.getElementById("phrase").value)
В ней считывается значение поле phrase. У меня подозрение, что огромный код до этих функций не выполняет ничего нужного и размещён просто чтобы запутать нас. Но в любом случае, хотим ли мы делать отладку всего кода либо только указанного фрагмента, главный вопрос — как именно в файл
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
вставить наш деобфусцированный код? Если не ответить на этот вопрос, то отладка обфусцированного кода может стать невозможной, так как предыдущий бессмысленный код может выполнять миллионы операций прежде чем управление перейдёт к действительно функциональному фрагменту.Выход из этой ситуации есть — причём функция изменения файлов на лету и сохранение этих изменений даже после перезагрузки страницы есть прямо в Инструментах разработчика браузера. Подробности смотрите в статье «
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
».Вставляем деобфусцированный код, ставим точку прерывания:
Добавляем эту страницу в «Save for overrides» как это описано в статье по ссылке выше и перезагружаем страницу.
Пожалуй, не буду разбирать этот пример до конца — будет это будет домашним заданием.
Заключение
На практике встречаются очень сложные для анализа варианты использования JavaScript, комбинирующие динамическое построение DOM структуры страницы, динамическое назначение событий, обфускацию кода, использование громоздких фреймворков и т. д. Но в целом исходить нужно из того, что защиты на стороне пользователя, в том числе с использованием JavaScript априори являются ненадёжными.
JavaScript может использоваться, например, для верификации данных в форме, но только для удобства пользователя, чтобы сообщить ему, что пропущено какое-то обязательное поле или введена информация в неверном формате. Но на стороне сервера всё обязательно должно перепроверяться так, как будто бы на стороне пользователя не было никаких проверок. Нужно исходить из того, что с пользовательской стороны может прийти что угодно.