LIMB
Вход Регистрация

тема

Взлом сети Tox DHT (0day) (2 часть)

ec5745y · · 11 ответов · 978 просмотров

Взлом сети TOX
Архитектурные уязвимости DHT
​

Ранее я писал про слив IP через UDP holepunching в Tox (https://forum.LIMB.cc/threads/6952/)
Проблема коснулась многих, почти никто не был в курсе того, как устроен Tox. Проблема решалась компиляцией ядра с отключением P2P функций TOX.
Это была не проблема Tox, а скорее пользователей, которые не до конца понимали, как Tox устроен.



В этой статье я расскажу про проблемы архитектуры сети Tox.
Обычно в статьях я публикую PoC, однако в связи с тем, что язвой болеют все пользователи Tox, а эта вдобавок ZeroClick и исправить её нельзя - сплоит будет опубликован через месяц. Надеюсь к этому моменту, все смогут найти альтернативы.
Для доказательства всех этих проблем на практике - я тебя взломаю:) Просто напиши свой TOXID на мой публичный ключ PGP.

Что позволяют эти уязвимости:
  1. Слив IP (Даже при использовании TCP relay).
  2. Слив TOX ID (за исключением NoSpam) и IP контактов, с которыми говорит пользователь в реальном времени, длина сообщений пользователя, время звонков или передачи файлов с их размером.
  3. Отключение пользователя от возможности общения с конкретными контктами, или вовсе со всеми.
Tox это отказоустойчивая альтернатива Skype с шифрованием, а не мессенджер для хакеров



Kademlia DHT
​
Tox использует Kademlia DHT для поиска контактов.

Как это устроено:
У каждого пользователя в сети есть свой DHT ID, который сформирован из TOX ID.

TOX ID — [32 байта публичного ключа][4 байта NoSpam][2 байта Checksum].
DHT ID — это просто публичный ключ из TOX ID.

В моём случае это

3C9E7149AF91441534CB2DD0F72EE26BA0A113944D8A1116AF74993E4888AE4D

Этот ID в DHT воспринимается как число, которое использует собеседник для поиска контакта.
Также пользователи выступают как DHT-ноды для поиска контактов.

Что происходит, если собеседник хочет найти меня?
Допустим, в списке его сохранённых нод есть 5 человек:


9B103BE19D8F80CD63CF604F9C3C572DB2A05931F5EF85A458B9436E5387D56D
B29349D85C4BC8D7B1569EF93859C85E481A92256D13D8AD6A729CDE1A6991AF
5528A7BCF061B3F2A80674247EA4FE04C1032BBD493538EBD9A09483D70FDA1D
DCE6951EF5DC21ED708977DC3E0CE92841F344BF69E0E426D1487AB573DB0379

3B67F01BD563E6ACA9C5B82A65882E06DAD9F5B231DEF24F8D8BE2C3BB1BBBF4

Он проверяет XOR дистанцию ID каждой ноды по отношению к моему ID и выбирает ближайшую
В данном списке ближайшая:

3B67F01BD563E6ACA9C5B82A65882E06DAD9F5B231DEF24F8D8BE2C3BB1BBBF4

Этой ноде человек передаёт искомый TOXID и получает от неё её ближайшую DHT ноду к указанному адресу.

Под дистанцией подразумевается лишь логическая XOR дистанция, а не физическая или сетевая.

После получения новой ноды (её ID, IP, порта и протокола TCP/UDP) пользователь продолжает поиски таким же образом, пока в конечном счёте одна из нод по такой цепи не выдаст ID, IP, порт, протокол TCP/UDP пользователя, чьи сетевые контактные данные хотел получить пользователь.
(В нормальном сценарии будет использован UDP, про который я говорил в предидущей статье, однако если UDP отключён, будет использован TCP релей, про что скажу ниже в статье.)
В реальности запрос идёт сразу к нескольким нодам, а в этом списке приблизительно от 20 до 50 нод.



1. Фейк DHT нода (Обращение К ПОЛЬЗОВАТЕЛЮ)
Основная проблема состоит в том, что DHT ID, которые используются для маршрутизации этих данных, хоть и нельзя создать произвольно, однако можно сгенерировать большое количество таких ID и выбрать из них те, которые гораздо ближе к конкретному человеку, чем это технически возможно при обычных условиях. Эти ноды, даже при небольшом их количестве, будут использоваться для маршрутизации по отношению к конкретному человеку в связи со своей близкой XOR дистанцией.


Например, при генерации 1.700.000 адресов у меня получился такой ID

3c9e74968a7b8b86307209844671906fb1744bc6b8545ef87315bfb0f0434081
Bucket index: 21

Его XOR дистанция по отношению к моему адресу в десятиричной системе по сравнению со случайными адресами.

100723917681807602704368452940352576123037377753433545265888869035357468558363 
43031670378353934529081025907356450668766808230877070555438974806224874528448
36279535870573277580374339531548263060552898104778368012815564143793027492362
110436929507253851350410508022199895464143634904104694550351965032428246991635
40524792991094776447345926343755666425520585992869261999603603162787532          <-- generated


Как видите, дистанция на порядки меньше по сравнению с обычными адресами.
Проще говоря - можно заставить собеседников человека обращаться к своей ноде. Нода сможет видеть TOXID, IP тех кто к ней обратился, и те же данные того, с кем обратившийся хочет поговорить. 


2. Форсирование использования (Обращение ПОЛЬЗОВАТЕЛЯ)
Kademlia DHT устроен таким образом, чтобы хранить информацию лишь о ближайших нодах. Их число строго огранично.
Это позволяет сети перестраивать себя таким образом, чтобы по схеме которую я описал выше, было проще найти пользователя.
Всего может быть сохранено по 8 нод в 128 бакетах = 1024 ноды. 
Бакет считать можно  так: берём XOR из двух ID, смотрим где находится самый старший бит, который отличается.
В сгенереной ранее ноде, бакет был 21. Чем значение выше, тем приоритетнее.
На практике же, я бился в разные ноды очень долго, просто как активный пользователь... Самый высокий бакет у меня - 6, а нод собралось всего 40...
Если бакеты пользователя забить нодами на разных значениях - он будет взаимодействовать с сетью лишь через подконтрольные ноды, как и другие люди с ним


3. Onion routing? Адекватного Routing вообще не существует...
Вы возможно слышали о Onion в Tox. На самом деле он использован не для передачи ваших сообщений, а лишь для DHT, чтобы в обратной последовательности по нодам вернуть сетевые данные контакта. Это не имеет связи с Tor.

Учитывая проблемы выше, никакой нормальной маршрутизации не будет, пользователь вообще больше не увидит реальную tox сеть.
Пользователь обращается к ноде хакера c запросом о поиске контакта, нода находит у себя другие ноды, которые ближе по ID, как и в реальном сценарии.
Далее, в зависимости от мощностей и желаний атакующего:
1. Фейковая нода находит ближайшую ноду, генерирует НОВУЮ ноду, которая близка по ID, передаёт её пользователю. 
   На протяжении всей цепи, фейковые ноды будут сопровождать пользователя, пока в сети не будет найден собеседник.
2. DHT нода сама находит человека и сообщает пользователю данные, без лишних посредников.


4. TCP relays и P2P 
Некоторые пользователи это DHT релеи, а некоторые DHT релеи ещё и TCP релеи...
В нормальном сценарии, если NAT позволяет - пользователи будут соединены через UDP holepunching.

P2P
Однако, пользователь не может самостоятельно проверить свой IP. DHT релей может заменить его на другой, который будет служить как реле.
Это позволит поставить между пользователями "прокси", который ретранслирует им сообщения друг друга, хотя они считают, что у них P2P.

TCP
Если же NAT блокирует UDP - будет форсирован TCP.
Между пользователями будет лишь один tcp релей в одну строну. Всего их два для одного чата.
TCP релеями служат DHT, в настройках которых это выставлено. Фейковый DHT релей выставляет самого себя в качестве TCP relay.




Нельзя читать сообщения пользователей ведь они зашифрованы, однако благодаря этому можно узнать TOXID собеседников (за исключением NoSpam), IP пользователей, размер сообщений, осуществление передачи файлов или видеозвонков в реальном времени.
IP целевых контактов или пользователя может быть изменён, поэтому клиент может быть отключен от взаимодействия с некоторыми или вовсе со всеми контактами.


Напоследок... Не используйте session, jabber через omemo гораздо лучше. 
По секрету всему свету - скоро свой мессенджер выпущу

Автор @ec5745y
Специально для 
forum.LIMB.cc​
👍46 ❤️4 😁6 😮8 💯7
Взломаю по запросу!​
Это занимает время. Вы можете в ЛС написать на свой TOX ID и публичный ключ мне PGP (PGP в Информации о профиле).
Для проведения этой атаки нужна предварительная подготовка, а во время самой атаки, ваш аккаунт в TOX должен быть онлайн (приблизительно 15-30 минут), поэтому время проведения стоит заранее обсудить.
Только ВАС! а не людей которых хотите. Данные после взлома отправляю на TOX ID который был взломан.
👍5 ❤️1
Стоит кое что объяснить отдельно, ведь в статье была неточность.
Уязвимость позволяет узнать 32 байта публичного ключа TOXID, ведь он служит для маршутизации.
TOX ID — 
[32 байта публичного ключа][4 байта NoSpam][2 байта Checksum]

Восстановить полный TOXID из публичного ключа нельзя. NoSpam параметр создаёт 65536 возможных вариантов.
NoSpam необходим чтобы отправить пользователю заявку в друзья

С другой стороны, публичного ключа достаточно, чтобы найти никнейм на форумах, которому придналежит ключ.
Получить ник в самом токсе через ZeroClick не выйдет, даже если бы NoSpam был известен - человеку необходимо сперва принять заявку в друзья
👍3
Если TOX работает через TOR, как через socks?
👍1
ОтветAMD64

Если TOX работает через TOR, как через socks?

Без разницы. Это никак не поможет. Разве что реальный IP будет неизвестен. Остальные проблемы будут актуальны:
Ответec5745y

Нельзя читать сообщения пользователей ведь они зашифрованы, однако благодаря этому можно узнать TOXID собеседников за исключением NoSpam, IP пользователей, размер сообщений, осуществление передачи файлов или…

В том числе отключение человека от сети Tox
👍4
Годная статья, в ознакомительном-то формате и как релиз новой находки(с чем хочу поздравить автора отдельно, это очень большая, жирнющая дырка, которую вскоре разнесут далеко и повсюду, считаю, автор достойно провел анализ этого старого пиленного-недопиленного мессенджера). Но мне не сильно-то нравится на данный момент, ec5745, что столь жирной находке нет подтверждений валидности со стороны исходника(а со скринами у вас возникла конкретная проблемка). Стоит подавать материал детальнее и с обеих сторон, значит, в таком-то экстраординарном случае, так и поступим. Лишь выскажу свои мысли по поводу этого write-up'а по этой конкретной структурной язве..


mkdir ToxVuln; cd ToxVuln
git clone https://github.com/TokTok/qTox
git clone https://github.com/TokTok/c-toxcore


Берем для анализа, позже будем приводить доказательства и/или опровержения чего-то отсюда. Так скажем, "поглядим со стороны сырка", но не Дружба.

Kademlia DHT. В целом, математику процесса автор передал достаточно точно, как и структуру получившихся ключиков, - за это здесь стоит выразить отдельную благодарность. Могу лишь совсем немного уточнить, что TOX ID - штука "постоянная", в то время как DHT ID генерируется из TOX ID лишь в моменте когда надо заюзать механику DHT. И уже по DHT происходит поиск дружка, которому хочешь строчить в ЛС, пользователи действительно могут выступать нодами в этом процессе.
Но вот с чем я тут не соглашусь, так это с вот этой строкой:
Ответec5745y

Этой ноде человек передаёт искомый TOXID и получает от неё её ближайшую DHT ноду к указанному адресу.

Нет, кони так не ходят. Почему они так не ходят? Идем в c-toxcore, рассматриваем движуху с точки зрения "ядра" токса. Рассмотрим исходник DHT.c из директории toxcore, очевидно во многом отвечающий за реализацию токсовой системы Distributed Hash Table(ура децентрализации).
И у нас тут есть функция DHT_send_nodes_request. Рассмотрим ее детальнее:

Не удалось прогрузить медиа


Что мы имеем по итогу, исходя отсюда? При отправке реквеста в дефолтнейшем UDP-сценарии происходит следующая цепочка действий:
1) Тест на дурака, смотрим, не отправляем ли мы рек самому себе, через проверку DHT ключика, если DHT ключик совпадает с хозяйским, запрос не пойдет. Авторы сами оставили комменты по этому поводу, как мы видим.
2) Формирование ping-механики. Что это за штука? Это не та же пинг-механика, которая есть в токсе по дефолту(о ней чуть позже, та самая штука, отправляющая раз в 60 секунд пингу на узлы, дабы знать, онлайн они или оффлайн). Тут для начала копируется public_key узла и значение ip_port, соответствующее ресиверу(приемнику, говоря по-русски, а не по-амерски). Этот этап считай отвечает за то, что мы и какой ноде отправили, что мы у нее спросили(согласно правильному замечанию автора, отдельные люди тоже бывают DHT-нодами, так следует из самой механики токскора, о чем позже). И потом, когда ответ придет, он будет сопоставлен с сохраненным запросом с нашей стороны. Проще говоря, для установки соответствия nodes_response с тем, что мы у нее спросили. Сохраняется все, как мы видим, под plain_message через pack_nodes. PING_id у нас тут отвечает за ожидание, у нас есть конкретный узел, мы его спросили и ждем ответа. Время декларируется закрепленным PING_TIMEOUT в DHT.h. Дефолтно это 5 секунд(я раньше ошибался, т.к. не знал, как именно проходит запрос до ноды в формате сырка, так что перед автором извиняюсь за неточность в тохе, был не прав, помнится, об этом мы говорили).
В плэйне в сухом итоге складывается следующая картина: у нас есть искомый Pk(публичный ключик), есть его устойчиво регламентированная длина, но еще у нас есть определение sizeOf на ping_id, который в сухом итоге восьмибайтен. uint64_t видим же? Видим, у нас даже есть перепроверка, точно ли значение ping_id совпадает с этим требованием, если нет, мы все дропаем к чертям. Далее у нас присовокупляется одно к другому, криптопабликкей_сайз мы спаиваем с сайзоффом, насколько я понимаю, это все ради той же линии защиты от smuggling-техник и подделывания(по-человечески говоря), о которой они втирают в своих .md-доках. 32+8=40, айди тохи сюда не влезет ни при каком из обстоятельств. Вместо носпам-байтов и байтов чексамма мы отправляем сюда байты ping_id, по которым реквест верифицируется и в случае чего протухает(чтобы токс на нашем устройстве не схавал запрос, который "делал год назад", проще говоря; эдакая своеобразная защита от подделки подобного рода запросов, таймаут вышел - токс реджектит все запросы с этим значением).
3) Вызываем функционал sendpacket, все просто. Сформировали, шифруется у нас в сухом итоге только упомянутый выше plain, функционал, вызываемый для этого - это dht_create_packet. Видим по фрагменту сырка.

TOX ID сюда не влезет ни коим макаром, в классическом понимании работы qTox отправит удпшку, основные части которой - ключик DHT и отдельное значение ping_id, которое нам собсна нужно для того, чтобы выполнять некоторые защитные функции, среди которых, очевидно, есть защита от поддельных респонсов - мы не будем принимать ответы на вопросы, которые мы, очевидно, не задавали никому. Или на вопросы, которые уже нам и не нужны, таймаут вышел.. хз, хорошая ли аналогия, я стараюсь просто отобразить это понятнее. В любом случае, буду рад любым ответам. В tcp-плане мы тоже ничего дельного не увидит, и мы опять же оперируем довольно таки скудными данными(хоть этого уже и достаточно для того, чтобы начать паниковать)).

Посмотрим на точность тезиса про "логическую близость". И да, это действительно так. В этом плане атака работает так, как ее описывают, за определение "ближайших" отвечает довольно понятный фрагмент сорца, все так же прописано в DHT.c:

int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2)
{
    for (size_t i = 0; i < CRYPTO_PUBLIC_KEY_SIZE; ++i) {
        const uint8_t distance1 = pk ^ pk1;
        const uint8_t distance2 = pk ^ pk2;

  
      if (distance1 < distance2) return 1;
        if (distance1 > distance2) return 2;
    }
    return 0;
}


Есть также отдельные проверки, связанные с "здоровой конкуренцией" нод - таймауты и так далее. Это все можно увидеть все так же в сорце, содержится это все в DHT.c(являющимся основной частью логики работы с этой штукой).
Рассмотрим этот процесс. Первым делом опять идем в DHT.c, находим функционал do_ping_and_sendnode_requests:

Не удалось прогрузить медиа


И конкретно эта функция работает "поверщиком" имеющихся DHT-нод. В чем суть: мы изначально раз в какое-то время должны отправлять реквесты "гудовым" нодам, которые у нас выбраны в качестве DHT-нод. Тем самым, мы постоянно имеем список относительно актуальных и рабочих нод, знаем, какие ноды помЭрли, и когда их надо менять. Если прошлая функция, рассмотренная ранее, фундаментальна в работе с лукапами по DHT, то конкретно эта работает поверщиком, смотрит на качество нод, считает количество адекватно работающих нод.
Следует рассмотреть функционал добавления и замещения сам по себе. И за него у нас представлены следующие функции - replace_all, add_to_close, addto_lists:

Не удалось прогрузить медиа


Тут мы можем видеть комментарии от самих авторов, что конкретно делает эта функция. И конкретно replace_all в этом списке из трех функций, отвечающих за апдейт ваших DHT-нод, самый умный. Он по факту говоря решает, достойна ли нода хранения, и если не слишком-то и достойна, то он ее заместит на новую. А в более подробном формате он умеет проверять качество и близость узла(опять же, логическую, как мы видели в фрагменте выше), пересортировывать существующие списки, и в итоге заменять худшего. Заменяется или вытесняется, на ваше усмотрение, здесь именно узел, который поставило в результате сортировки в плохие.
Логика сортировки содержится в целом отдельно, в формате функционала sort_client_list, тесно связанного с компаратором - client_data_cmp. Можете рассмотреть его функционал, он довольно типичен.

add_to_close работает менее точечно и уже с бакетами: он может заменять пустые/помершие строки в бакетах на новый узел(DHT). Его структура куда проще: он просто сначала считает "bucket index", отличающийся бит между ключом ноды и ключом нас самих, и согласно индексу уже идет ддальше, определяя, в какой бакет упадет этот конкретный узел. Работа с бакетами этим не ограничивается: при проверке, если слоты померли, что выясняется через assoc4 и assoc6(по факту, тыкает по IPv4 и IPv6 и смотрит, есть ли ответ), он их меняет. Бакет индекс считается следующим образом:

unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2)
{
    unsigned int i;
    unsigned int j = 0;

    for (i = 0; i < CRYPTO_PUBLIC_KEY_SIZE; ++i) {
        if (pk1 == pk2) {
            continue;
        }

        for (j = 0; j < 8; ++j) {
            const uint8_t mask = 1 << (7 - j);

            if ((pk1 & mask) != (pk2 & mask)) {
                break;
            }
        }

        break;
    }

    return i * 8 + j;
}


В дефолтной конфигурации у нас есть LCLIENT_LENGTH, равный 128(он регламентирует число бакетов), и есть количество нод на бакет, в дефолте 8. В сухом итоге описать можно так: чем раньше по старшим битам ключ отличается от свойского-хозяйского, тем меньше бакет индекс.

addto_lists в этом плане наиболее глобален и работает с узлами на уровне решения вопросов, куда в целом запишется новый узел. И дергает он для этого выше описанные две функции.
Смысла сильно разбираться с упомянутыми компараторами здесь нет, основа основ - архитектурное решение, при котором считается именно логическое расстояние меж узлами. Мы разбирали его чуть выше. Также, стоит заметить, что решение автора подбирать наиболее подходящий индекс здесь подкреплено структурой и логикой самого ядра)). Все из трех описанных выше фундаментальных функций по работе с узлами DHT учитывают прежде всего показатели индекса, и чаще всего "самое ближнее место" будет заполнено не до конца, что создает нам комфортные условия для атаки "вытеснения", при которой мы вытесним DHT-узлы своим, возможно, более качественным, но точно - более "близким", ведь мы подбираем его искуственно. Про логику подбора и компараторы разглагольствовать, как сказал выше, не планирую: мы учли главное - механику замещения DHT-нод. А остальное не так важно, ведь все строится по факту на следующих основных принципах:
1) Индекс(мы видели, как он считается)
2) Наличие ответа от ноды
3) Если ответ от ноды есть, то качество ноды(согласно своим критериям сравнения).

Но как итог, в нужные слоты попасть более чем возможно, - атака более чем выполнима. Даже если все записи в конкретном бакете живые, почему бы и не заменить кого-то среди них "хуже нас"? Их 8, по теории вероятности мы выигрываем, и атака становится не столь затратна математически.

Но однако, в пари с логикой уже обработки нодой конкретного запроса высказывание про то, что нода изначально оперирует полным TOX ID, неверно. Изначально она оперирует ключами, о коих речь уже была чуть выше, и она безусловно может видеть, кто и что запросил(IP:PORT сурс-данные).
Но цельный и полный TOX ID в его книжном определении, где будет установлен в т.ч. noSpam-сегмент, никак нодой быть перехвачен не может. Даже в формате TCP-релея нода явно не видит таких данных и оперирует только данными, среди коих IP:pORT, ключик, да и прочая подкапотная фигня, с реальным мужицким OSINT не имеющая ничего общего. Однако, нам прекрасно известно, как генерируются эти ключи, и тут цельный TOX ID нам особо и не нужен.
Именно поэтому первым делом решил подумать со стороны наличия того самого noSpam-сегмента. Из интереса продолжим некий "пентест" токса.

Еще один вектор, который стоит просматривать в такой ситуации, - "а что происходит, когда меня добавляют в друзья?". Да, все верно, если поменять адрес в его noSpam-части, а к тебе захотят добавиться по адресу прошлому, ничего не выйдет. И тут важный нюанс, который придет в голову любому, скажем так, пентестеру, - а как именно тебя "отвергает" от добавления в друзья по старому, "засвеченному" адресу? Решил подумать не со стороны задротства в Wireshark и написывания самому себе, а со стороны исходников: где же чисто в теории может светануть фулловым токс айдишником? Цельной адресной записью, где есть тот самый заветный NoSpam-сегмент, а не гольная DHTшка.
Как мы знаем, изначально nospam может быть описан как механика от авторов тохи, которая нацелена на легкое изменение своего адреса без потребности создавать новый профиль, полезная тем, чей условный TOX ID например слили и теперь херачат в него с кучи разных учеток, или просто если надо уйти от нежелательных контактов. Если поменять nospam-фрагмент ключа, что конкретно будет происходить? Как это все реализовано в ядрышке? Постараюсь рассмотреть этот момент без засирания треда скринами из сурсов, пойдем только по самому важному.
Как мы знаем, если наш дружок сменит noSpam-фрагмент из своего Tox ID через встроенный в клиенты опционал, мы не потеряем его из контактов, он будет у нас все так же в формате друга, мы сможем и дальше продолжать с ним общение сообщениями. И это важный нюанс, который сразу стоит запомнить перед анализом со стороны архитектуры этого процесса и роли noSpam-идентификатора в TOX ID в суммарном смысле. 

А ответ  на изначальный и очевидный вопрос о френд реквестах как векторе прост. Эта проверка происходит уже со стороны потенциального "друга", и прописана она в friend_requests.c:

static int friendreq_handlepacket(void *_Nonnull object, const uint8_t *_Nonnull source_pubkey, const uint8_t *_Nonnull data, uint16_t length, void *_Nonnull userdata)
{
    Friend_Requests *const fr = (Friend_Requests *)object;

    if (length <= 1 + sizeof(fr->nospam) || length > ONION_CLIENT_MAX_DATA_SIZE) {
        return 1;
    }

    ++data;
    --length;

    if (fr->handle_friendrequest_isset == 0) {
        return 1;
    }

    if (request_received(fr, source_pubkey)) {
        return 1;
    }

    if (memcmp(data, &fr->nospam, sizeof(fr->nospam)) != 0) { **
        return 1;
    }

    if (fr->filter_function != nullptr) {
        if (fr->filter_function(fr->filter_function_userdata, source_pubkey) != 0) {
            return 1;
        }
    }

    addto_receivedlist(fr, source_pubkey);

    const uint16_t message_len = length - sizeof(fr->nospam);
    VLA(uint8_t, message, message_len + 1);
    memcpy(message, data + sizeof(fr->nospam), message_len);
    message[message_len] = 0;
    fr->handle_friendrequest(fr->handle_friendrequest_object, source_pubkey, message, message_len, userdata);
    return 0;
}


Проверок здесь на самом деле достаточно. Мы видим и проверку длины пакетика, и проверку на дубли с интеграцией функционала request_received, чуть ранее регламентированного, и проверка, есть ли этот кент уже в нашем списке. Но основная интересующая нас проверка - это  фрагмент, помечченный здесь звездочкой. Тут мы смотрим, совпадает ли наш фрагмент(из локально хранимого TOX ID) с фрагментом в Friend Request(запросе на дружбу).
Сравниваем байты, если не совпадает, клиент все отвергает. Становится понятно, зачем в голову приходит иддея первым делом это проверить: мы ищем слабые места, в которых может протечь фулл айдишка, в исходниках и функционале. Как при проверках на IDOR.. проверяешь всю логику, авось что-то да найдешь. Общение же в прочих форматах остается таким же, и "где твой собеседник" нода определяет, опираясь на DHT ID.

И да, в таком случае фрагмент Friend Request может пролететь через onion и даже через tcp-релеи(например, если до получателя по UDP не достучаться). Но так как friend request сам по себе летает в сугубо зашифрованном виде, толку нам с такого будет скорее всего как с козла молока. При инициализации же его отправки система определяет, в каком формате его лучше пульнуть:

int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint32_t nospam_num, const uint8_t *data,

                               uint16_t length)

{

    ...

    const uint16_t packet_size = 1 + sizeof(nospam_num) + length;

    VLA(uint8_t, packet, packet_size);

    memcpy(packet + 1, &nospam_num, sizeof(nospam_num));

    memcpy(packet + 1 + sizeof(nospam_num), data, length);


    if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) {

        packet[0] = PACKET_ID_FRIEND_REQUESTS;

        return write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, packet, packet_size,

                                 false) != -1 ? 1 : 0;

    }


    packet[0] = CRYPTO_PACKET_FRIEND_REQ;

    const int num = send_onion_data(fr_c->onion_c, friend_con->onion_friendnum, packet, packet_size);


Тут все читаемо даже тем, кто особо ядро не анализировал. Если уже есть френд-коннект, шлется в его формате, иначе - с использованием Onion-роутинга. И да, в обоих случаях применяется шифрование, и полный адресок в положении "посредника" увидеть не представится возможным. Короче говоря, да, мы видим ключик, но и большего нам так то и не нужно. Ключика вполне себе хватает). Полный TOX ID в случае такой атаки мы не получим, но он и не сильно-то нам в итоге и нужен.
Здесь же кроется еще одна неточность про onion-роутинг в статье - да, через него не только DHT летает, а как же френд реквесты xDDD

Почему именно так? В состоянии что TCP-релея, что DHT-релея запрос с полным TOX ID мы никак не увидим в клиртекст-формате(DHT-релеи в принципе для этого не созданы, а в случаях отсутствия френд_коннекта все все таки криптуется, и толку с этого не будет). Полноформатный TOX ID не фигурирует ни в принципе работы DHT, ни в принципе работы TCP Relay. Френд-реквесты как отдельный вектор при валидации уязвимости, разобранный нами выше, также на фулловый TOX ID не протечет.
Сами по себе TCP-релеи действительно в некоторых ситуациях могут "побыть прокси", и будут в состоянии посредника, который знает оба "конца" этого диалога. Такой функционал есть в дефолтном формате.
Как итог, я думаю, что пользователь @ec5745y более чем достоин выплаты за свою находку, как и достоин отдельной благодарности от всех заядлых юзеров qTox, которым раскрыли глаза на фундаментальную уязвимость их любимого мессенджера. Автор довольно точно описывает фундаментальные принципы работы этого софта за исключением пары деталей, одна из которых и уточняется в данной рецензии - в открытом виде ни в TCP-формате работы, ни в UDP-формате работы ничего в полном TOX ID-формате не течет. Однако, уточнение про "полноту" получаемых кредов стоило внести изначально, что на данный момент пользователь по своему согласию и сделал, дабы сделать свой райтап еще более фактологически точным:

Не удалось прогрузить медиа




В остальных деталях статья правдива, и она действительно описывает новую уязвимость, провалидировать ее может все так же каждый. Про то, как происходит "вытеснение", рассуждать тоже сильно не приходится: мы же изначально втюхиваем клиенту свою поганенькую ноду, которую мы себе получаем методом перебора. Можете передавать на оплату.

Я кстати пошаромыжился на википедии и нашел вот такое, оказывается, давно выдуманное описание для таких атак, стоит сюда закрепить, наверное:


Не удалось прогрузить медиа
👍5 ❤️1
ОтветAMD64

Если TOX работает через TOR, как через socks?

Адрес "сольет" с места, которое уже нельзя байпасснуть. Если он под виртуалкой в режиме NAT-сети в виртуализаторе, а на хостовой машине VPN, то сольется адрес VPN. Ежели из-под тора, то сольется торовый айпи, айпи экзит ноды. Там в целом-то UDP не проходит, и простую атаку на "втуливание" DHT провести уже будет проблематичненько. Да и UDP Holepunching произвести будет проблематичнее: логика такова, ежели мы не можем прошить NAT, мы соединяемся по альтернативным путям.

Куда тяжелее живется людям, которые ставят вдобавок устаревший клиент(первый и официальный) токса. Там в принципе есть некая форма "игнора" правил работы с UDP, и если быть не за TCP-only сетью, или допустим не за виртуалкой на NAT, то игнорировать он будет уже не только настройки, связанные с UDP, но и существующие туннели.
Ответec5745y

По идее, можно взять DHT ноду которую использует клиент и ддоснуть её, чтобы он перестал её использовать. Гляну немного потом, есть ли какая=то защита от этого :)

Да ты и так и так в математическом выигрыше, среди 8 найдется как минимум 3 строки, которые будут хуже твоей строки, в средней-то ситуации.. децентрализованные сети, куча полуживых нод. У токса очень широкий взгляд на вопрос замены узлов, что я и попытался отметить в своем кратком замечании, он найдет куда подсунуть твою ноду.
👍3
ОтветleakinN_nname

В дефолтной конфигурации у нас есть LCLIENT_LENGTH, равный 128(он регламентирует число бакетов), и есть количество нод на бакет, в дефолте 8. В сухом итоге описать можно так: чем раньше по старшим битам ключ отличается…

128 бакетов это полный бред, ну не может быть настолько больших совпадений. 
Думаю 8 бакетов это максимум, который может быть забит (64 ноды), далее уже пойдёт перезапись.

ОтветleakinN_nname

Тем самым, мы постоянно имеем список относительно актуальных и рабочих нод, знаем, какие ноды помЭрли, и когда их надо менять.

По идее, можно взять DHT ноду которую использует клиент и ддоснуть её, чтобы он перестал её использовать. Гляну немного потом, есть ли какая-то защита от этого :)
Ещё одна уязвимость.... Ну так, если старые придумают как исправить :) (не придумают)


Большое спасибо за коммент и детальный разбор <3
👍2
Ответec5745y

128 бакетов это полный бред, ну не может быть настолько больших совпадений. Думаю 8 бакетов это максимум, который может быть забит (64 ноды), далее уже пойдёт перезапись. По идее, можно взять DHT ноду которую использует…

Именно поэтому там есть механика "прижимки", которая отрабатывает так, что с высшими среди прочих значениями индекс бакета энтри полетят поближе к своим собратьям. И так будет заполняться дальше. Чтобы посмотреть, сколько их вообще доступно, советую проглядеть константы в конфиге DHT, DHT.h.

Касательно прочих моментов, стоит не путать френд-лист с клоуз-листом. На френд-листе конфиг таков, что как ни прыгай, больше 8 из конфига ты не сделаешь на друга записей(DHT-нод). Тут и сама вероятность против тебя будет работать. Да и в целом ноды из френд-листа ведут себя немного иначе и могут выступать, допустим, как DHT-нодами для лукапа, так и route-нодами - то бишь, быть посредниками, когда из-за каких-то обстоятельств(выключенный в норме удп, например)) вы не можете с другом тет-а-тет говорить.
👍3
Ответec5745y

Взлом сети TOX Архитектурные уязвимости DHT ​ Ранее я писал про слив IP через UDP holepunching в Tox ( https://forum.LIMB.cc/threads/6952/ ) Проблема коснулась многих, почти никто не был в курсе того, как устроен Tox…

Можете, пожалуйста с практической стороны продемонстрировать
1. Взять отключить человека от сети tox
2. Слить TOX ID, с которыми говорит пользователь в реальном времени, длину сообщений пользователя, время звонков или передачи файлов с их размером.

Пока выглядит материал как больше теоритический из разряда атака 51% с биткоином))
👍1
Ребятки, если вам непонятно то, как устроены эти уязвимости, я объясню по очень простому.
Напишите в ЛС или спросите прям тут, если вам это нужно
👍1

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.