Динамический анализ 44 273 WeChat и 2 721 Baidu Mini-Programs выявил 1 834 случая кривой реализации OAuth-подобной авторизации - 619 приложений содержали комбинацию нескольких дыр одновременно (Shi et al., "MiniCAT: Understanding and Detecting OAuth Access Control Threats in Mini-Programs», USENIX Security 2024). Среди пострадавших - государственные сервисы и медицинские Mini-Programs, через которые утекали национальные ID и медицинские записи граждан КНР. Результаты подтверждены присвоением идентификаторов CNVD/CNNVD (конкретные ID не раскрыты публично).
В русскоязычном пространстве эта тема - белое пятно. Все существующие материалы крутятся вокруг стандартного OAuth 2.0: redirect_uri bypass, token theft через Slack и Facebook. Но проприетарный протокол суперприложений - совершенно другой зверь, и его никто не разбирал. Ниже - три конкретных вектора account takeover в WeChat Mini-Programs: от модели угроз до PoC-логики и инструментария динамического анализа.
Как устроена авторизация в WeChat Mini-Programs и почему это не стандартный OAuth 2.0
Разработчики привыкли называть механизм входа в Mini-Programs "OAuth", но это вводит в заблуждение. WeChat использует проприетарный протокол - OAuth-based Authentication (OBA), и он принципиально отличается от RFC 6749.
В классическом OAuth 2.0 flow участвуют три стороны: Resource Owner, Authorization Server и Client Application. Клиент получает authorization code через
redirect_uri, обменивает его на access_token с помощью client_secret и дальше ходит с токеном к API ресурсного сервера. Основная attack surface - манипуляция redirect_uri (Open Redirect -> Token Theft), отсутствие или предсказуемость state-параметра, слабая валидация PKCE.В WeChat Mini-Programs архитектура другая. Mini-Program выполняется внутри уже аутентифицированного суперприложения (по отчёту Tencent за Q3 2024 - около 1,38 млрд combined MAU Weixin/WeChat). Workflow OBA:
- Фронтенд Mini-Program вызывает
wx.login(), получая временный одноразовыйcode(не authorization code в терминах OAuth 2.0 - у него другой lifecycle) - Бэкенд разработчика отправляет
codeвместе сappidиappsecretна эндпоинт WeChatcode2Session(server-to-server) - WeChat возвращает
openid(уникальный идентификатор пользователя в рамках конкретного Mini-Program),unionid(идентификатор в рамках всех приложений одного разработчика) иsession_key(криптографический ключ сессии для расшифровки пользовательских данных) - Бэкенд разработчика создаёт сессию и отправляет фронтенду session token
redirect_uri здесь нет - Mini-Program живёт внутри WebView суперприложения. Стандартные чеклисты OAuth-аудита (проверка redirect_uri, state, PKCE) бесполезны. Attack surface смещается на обработку session_key и openid на стороне бэкенда разработчика, клиент-серверное взаимодействие между фронтендом Mini-Program и сервером разработчика, а также криптографические операции с данными пользователя.Проприетарная природа протокола делает WeChat OBA слепым пятном для тех, кто привык к стандартному OAuth 2.0. И это не академическая проблема - это реальная attack surface, которую мало кто проверяет.
Модель угроз: кто атакует и что ему доступно
[Применимо: внешний пентест Mini-Program, black-box / bug bounty]Перед разбором векторов - модель угроз. Атакующий находится в позиции внешнего исследователя без доступа к серверной инфраструктуре разработчика.
Атакующий может: создать собственный Mini-Program и получить
appid/appsecret; перехватывать HTTPS-трафик между фронтендом Mini-Program жертвы и бэкендом разработчика (через mitmproxy/Burp Suite с настроенным trust store на рутованном устройстве); анализировать и модифицировать клиентский JavaScript-код Mini-Program (через WeChat DevTools или jadx для деобфускации); слать произвольные запросы к API бэкенда разработчика.Атакующий не может (при корректной реализации): перехватить трафик между бэкендом разработчика и серверами WeChat (server-to-server вызов
code2Session); подделать code от wx.login() - он одноразовый, TTL порядка 5 минут согласно документации code2Session, привязан к appid Mini-Program и сессии wx.login(), invalidated после первого вызова code2Session (повторный вызов возвращает errcode 40163 - code been used, см. developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html); получить session_key напрямую от WeChat без знания appsecret.Вся attack surface возникает из-за ошибок на стороне сторонних разработчиков, а не платформы WeChat. Платформа предоставляет API - разработчики криво их реализуют. Принципиальное отличие от стандартных OAuth-уязвимостей, где проблема часто в самом Authorization Server.
Вектор 1 - Client-Side Identity Forgery: утечка credentials на фронтенд
Суть: бэкенд разработчика возвращаетopenid и/или session_key прямо в HTTP-ответе фронтенду Mini-Program. Атакующий перехватывает эти данные и подставляет openid жертвы для авторизации под чужим аккаунтом.Механика уязвимости. В корректной реализации вызов
code2Session идёт server-to-server: бэкенд получает openid + session_key от WeChat, создаёт сессию и возвращает фронтенду только непрозрачный session token (cookie или JWT). Фронтенд никогда не видит ни openid, ни session_key.В уязвимой реализации бэкенд сливает эти данные прямо в теле ответа:
JSON:
{
"status": "ok",
"openid": "oXyz123abc456def789",
"session_key": "tK8dN2sLm...",
"user_token": "eyJhbGciOi..."
}
Цепочка эксплуатации. Атакующий запускает целевой Mini-Program, проходит авторизацию и перехватывает через Burp Suite собственный
openid из ответа. В дальнейших запросах к API бэкенда видит, что openid передаётся как параметр аутентификации (в теле POST или query-параметре). Подстановка чужого openid приводит к авторизации от имени жертвы - если бэкенд не привязывает openid к серверной сессии, запрос проходит без дополнительных проверок.Предусловия: бэкенд передаёт
openid на фронтенд в ответе на логин-запрос; openid используется как единственный идентификатор пользователя без привязки к серверной сессии; нет дополнительной верификации (подпись запроса, привязка к device fingerprint).Когда техника НЕ работает: разработчик реализовал серверный session management и не раскрывает
openid клиенту; Mini-Program не имеет собственного бэкенда (чисто статические информационные приложения); бэкенд проверяет code при каждом запросе, а не полагается на ранее полученный openid.По MITRE ATT&CK - комбинация Steal Application Access Token (T1528, Credential Access): перехват
openid/session_key из трафика, и Valid Accounts (T1078): использование украденного идентификатора как валидных учётных данных.Вектор 2 - Статические идентификаторы и перманентный захват аккаунта
Суть: Mini-Program использует для аутентификации статический идентификатор (openid или unionid) вместо динамически генерируемого токена сессии. Атакующий, однажды узнав идентификатор жертвы, получает постоянный доступ к аккаунту - без ограничения по времени.openid не меняется между сессиями для одного пользователя в одном Mini-Program. Если бэкенд принимает openid напрямую как ключ аутентификации (вместо короткоживущего session token), атакующий получает перманентный доступ. По сути - вечный пароль, который нельзя сменить.Ситуация становится хуже при использовании
unionid - идентификатора, общего для всех Mini-Programs одного разработчика. Компрометация unionid в одном приложении даёт доступ ко всем приложениям этого разработчика. Это уже не просто account takeover - это lateral movement через Application Access Token (T1550.001) внутри хозяйства одного вендора.Разница с Вектором 1: в первом случае проблема - утечка
openid через сетевой трафик. Здесь проблема глубже: даже если openid получен легитимным путём (утечка базы данных, социальная инженерия, перехват трафика другого Mini-Program того же разработчика через unionid), отсутствие ротации сессий делает его вечным ключом к аккаунту.Предусловия:
openid или unionid используется как единственный фактор аутентификации; нет серверной сессии с ротацией токенов; бэкенд не привязывает аутентификацию к session_key (который обновляется при каждом вызове wx.login()).Когда техника НЕ работает: разработчик реализовал нормальный session management с короткоживущими JWT/cookie, привязанными к
session_key; бэкенд требует свежий code от wx.login() при каждом критическом действии; Mini-Program не хранит пользовательских данных.По OWASP - A05:2021 Security Misconfiguration: архитектурная ошибка при проектировании auth-flow.
Вектор 3 - Криптографический дефект: утечка session_key через Initialization Vector
Суть: платформенный дефект позволяет восстановитьsession_key через анализ Initialization Vector (IV) в зашифрованных данных пользователя. Это открывает расшифровку чувствительных данных (номер телефона, профиль) и подделку личности.Механика уязвимости. Когда Mini-Program запрашивает чувствительные данные (номер телефона через
getPhoneNumber, информацию профиля), WeChat возвращает их в зашифрованном виде - AES-128-CBC с session_key в роли ключа. Данные передаются вместе с encryptedData и iv.В корректной криптографической реализации IV должен генерироваться случайно и быть независимым от ключа. Исследование обнаружило, что на платформе Baidu (порядка 600–700 млн MAU) IV переиспользует часть session_key вместо генерации случайного значения. Что это значит на практике: атакующий получает IV из ответа (он передаётся открыто), IV содержит информацию о
session_key, и пространство перебора для восстановления полного ключа существенно сокращается. Красивый фантик шифрования - а внутри подсказка к ключу. По OWASP - A02:2021 Cryptographic Failures.Для WeChat ситуация несколько лучше - IV генерируется отдельно от
session_key. Но если session_key утёк через Вектор 1, атакующий расшифровывает все пользовательские данные, включая номер телефона - а это уже вектор для account takeover через SMS-подтверждение на других платформах или внутри WeChat.Предусловия: для Baidu - доступ к зашифрованным данным с IV (перехват трафика); для WeChat - утечка
session_key через Вектор 1 или 2; Mini-Program запрашивает чувствительные данные (телефон, профиль).Когда техника НЕ работает: дефект с IV обнаружен на Baidu, не на WeChat. Для WeChat Mini-Programs этот вектор работает только в комбинации с утечкой
session_key - сам по себе он не эксплуатируется. По MITRE ATT&CK - Exploitation for Credential Access (T1212).Сравнение векторов и место в kill chain
| Параметр | Вектор 1: Client-Side Forgery | Вектор 2: Статические ID | Вектор 3: Крипто-дефект |
|---|---|---|---|
| Attack Surface | HTTP-ответ бэкенда фронтенду | API бэкенда (session management) | Зашифрованные данные (encryptedData + IV) |
| Предусловия | Утечка openid/session_key в трафике | Отсутствие ротации сессий | Дефект IV (Baidu) или утечка session_key |
| Импакт | Full account takeover | Persistent account takeover | Расшифровка PII + account takeover |
| Сложность эксплуатации | Низкая - перехват и replay | Средняя - нужен openid жертвы | Высокая - криптоанализ + чейнинг |
| MITRE ATT&CK | T1528 + T1078 | T1078 | T1212 |
| OWASP 2021 | A05 Security Misconfiguration | A05 Security Misconfiguration | A02 Cryptographic Failures |
Kill chain позиция. Все три вектора работают на стыке Initial Access и Credential Access:
- Initial Access: T1190 (Exploit Public-Facing Application) - эксплуатация уязвимого бэкенда Mini-Program
- Credential Access: T1528 (Steal Application Access Token) - перехват
openid/session_key; T1212 (Exploitation for Credential Access) - эксплуатация крипто-дефекта - Lateral Movement: T1550.001 (Application Access Token) - использование
unionidдля доступа к другим Mini-Programs того же разработчика - Persistence: T1078 (Valid Accounts) - статический
openidобеспечивает постоянный доступ без ротации
Кросс-платформенная картина. Аналогичные мисконфигурации воспроизводятся не только в WeChat, но и в Baidu, Alipay и TikTok Mini-Programs. Это системная проблема среды суперприложений, где платформа предоставляет API, а тысячи сторонних разработчиков с разным уровнем компетенции собирают авторизацию самостоятельно.
Динамический анализ Mini-Programs: инструменты и методология
Требования к окружению:
- ОС: Windows 10/11, macOS 12+, или Ubuntu 22.04+
- RAM: минимум 8 ГБ (16 ГБ рекомендуется для одновременной работы эмулятора и прокси)
- WeChat DevTools: текущая версия (developers.weixin.qq.com)
- Burp Suite Professional >= 2024.x (Community Edition ограничен в кастомных расширениях для wx-payload)
- mitmproxy >= 10.x
- jadx >= 1.5 для декомпиляции обфусцированного JavaScript
- Python 3.10+ (requests, pycryptodome для работы с AES)
- Android-устройство с root или эмулятор (для перехвата трафика WeChat)
- Сетевые условия: онлайн (нужен доступ к серверам WeChat и бэкенду тестируемого Mini-Program)
code2Session, как обрабатывает openid при повторной аутентификации - это видно только в трафике. SAST тут мёртв.Методология ручного пентеста Mini-Program OBA:
- Настройка прокси. На Android установка CA-сертификата в system trust store требует root - Android 7+ с targetSdkVersion>=24 по умолчанию не доверяет user-installed CA (Network Security Config), и WeChat собран с этим ограничением. Альтернатива: Frida-скрипт для отключения SSL verification в WeChat runtime (работает без модификации system store, но требует root/jailbreak). На iOS - профиль конфигурации через Settings.
- Идентификация OBA-запросов. Открываем целевой Mini-Program и фильтруем в логе прокси запросы, содержащие параметры
code,openid,session_key,encryptedData,iv. Маркер OBA-flow - POST-запрос сразу после загрузки Mini-Program сcodeотwx.login()в теле. - Проверка утечки (Вектор 1). Анализируем ответ бэкенда на логин-запрос. Если в JSON-ответе присутствуют ключи
openidилиsession_key- уязвимость подтверждена. Автоматизация через mitmproxy addon:
Python:
# mitmproxy addon: обнаружение утечки OBA-credentials
# (пример для демонстрации концепции)
from mitmproxy import http
import json
def response(flow: http.HTTPFlow):
if not (flow.response and flow.response.content): return
try:
data = json.loads(flow.response.content)
leaked = {k: data[k] for k in ("openid","session_key","unionid") if k in data}
if leaked: print(f"[OBA LEAK] {flow.request.url}: {leaked}")
except: pass
- Тестирование подмены (Вектор 2). В Burp Repeater повторяем запрос аутентификации с подменённым
openid. Если сервер возвращает данные другого пользователя без дополнительных проверок - подтверждён Вектор 2. - Проверка криптографии (Вектор 3). Если Mini-Program запрашивает номер телефона - перехватываем
encryptedDataиiv. Сравниваем IV между сессиями: статичный IV или корреляция сsession_keyуказывает на криптографический дефект.
wx.login()). Для полноценного тестирования нужно реальное устройство с установленным WeChat. Burp Suite Community Edition не поддерживает кастомные extensions для декодирования WeChat-специфичных payload - Professional версия обязательна для серьёзного аудита.Чеклист безопасности OBA для разработчика и аудитора
Нумерованный список действий для разработчика Mini-Program, аудитора или пентестера - в формате, пригодном для включения в отчёт:
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
1 834 уязвимых Mini-Program из ~47 000 проанализированных - около 4%. Звучит терпимо, пока не вспомнить, что WeChat хостит, по разным оценкам, несколько миллионов Mini-Programs. Экстраполяция грубая, но порядок понятен: десятки тысяч приложений с дефектами аутентификации, обрабатывающих платежи, медицинские данные и государственные сервисы.
Корневая причина не в сложности протокола - WeChat OBA проще стандартного OAuth 2.0. Причина в том, что среда суперприложений создала ситуацию, где разработчик доставки еды реализует криптографическую привязку сессии к
session_key. Это не его компетенция, и никакая документация это не исправит. Платформа должна брать безопасность аутентификации на себя - через server-side enforcement на уровне code2Session API, а не рекомендации в dev docs, которые большинство разработчиков не читают.Второй неудобный факт: статический анализ для этого класса уязвимостей мёртв. MiniCAT показал, что динамический подход обнаруживает несопоставимо больше проблем, чем любой SAST. Если исследуете Mini-Programs - без настроенного mitmproxy и автоматизации перехвата вы пропустите подавляющее большинство находок. И это касается не только WeChat: Alipay, TikTok, Baidu - тот же архитектурный паттерн, те же ошибки. Рынок суперприложений растёт, а количество исследователей, разбирающихся в специфике их OBA-протоколов, можно пересчитать по пальцам одной руки. Если хочется разобрать OAuth-цепочку от утечки openid до полного account takeover на живом стенде - web-категория на HackerLab.pro (https://hackerlab.pro) для этого подходит.
Последнее редактирование модератором: