вах
March 25

Binance (и coinmarketcap) соло с бананом. Шучу, не соло. Geetest на запросах.

Пожалуйста, не проматывайте до середины

В этом материале я разобрал один из своих заказов, то как решал авторизацию (binance и coinmarketcap) и регистрацию (coinmarketcap) аков на запросах, их алгоритмы шифрования, а также реверс geetest slider и его решение на запросах без сервисов (онли нода, онли бас + шифрования).

Однако не все сразу, начнем чуть издалека.

Binance

Шифрования и подписи на авторизации

Понадобился мне как-то бинанс, но копировать куки из браузера было не очень хорошим решением, тк они автоматически умирали через сутки. Да, можно было повесить браузер, делать авториз и работать с ним, но мне захотелось сделать авториз на запросах, потому-что могу. А еще, через браузер, почти всегда, получается какое-то не стабильное говнище.

Начнем с конца.

Для проверки авториза и работы на аке достаточно вот этого. Я ручками взял и удалял все куки, заголовки и тд. Все остальное по сути мусор.

Тоесть достаточно всего одного кукиса p20t, который серв при авторизе дает и csrftoken (который всего-то нужно как-то получить). Хорошо, получается нам нужно узнать от куда берутся всего 2 параметра)

И я сел серчить. Когда я тыкал, пост параметры выглядели следующим образом

Его линка https://accounts.binance.com/bapi/accounts/v2/public/authcenter/login

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

Тоесть вот так выглядят пост данные, девайс инфо - фингер, мыло, пароль, сейф пароль и капча.

Ну оки, запросим тогда капчу. Ключ для капчи получаем так
"https://accounts.binance.com/bapi/composite/v1/public/common/security/gt-code?t="+Date.now()
Апи дока по решению https://rucaptcha.com/api-rucaptcha#solving_geetest

deviceInfo - прост фингер. по итогу оно на беке будет чекать, юа ласт авториза совпадает или нет. Тоесть в случае спиздинга куков будет фрод, тк не тот юа.

password - md5(password+email)

Ну и я такой: О, md5!

А что если бы было сложнее?
Ну воть, зашел в функцию и опять очевидно что это md5.

safePassword - sha512(password). Да, это тоже сразу очевидно..

Если вдруг, по какой-то причине это не очевидно, тыкаем дальше брек и тут прямо в коде это написано

Потыкать можно туть: https://emn178.github.io/online-tools/md5.html

А потом я еще словил.. validateCodeType: "random". Не пон, защита це или нет, капчу решать я понимаю как, ее слать и буду.

В запросеках есть вот такое. Это обычный uuid. Но эт не обязательно, чисто украшательства для статки бинанса.
x-ui-request-trace: 74274726-6d01-48fc-b885-7eb578840b84
x-trace-id: 74274726-6d01-48fc-b885-7eb578840b84

Генерю так
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-bxxx-xxxxxxxxxxxx'.replace(/[xb]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
VAR_UUID=uuidv4();

Я: наверное бинанс использует какие-то хитрые методы для индентификации юзера, а также при авторизе супер шифруют и потом как-то разгадывают...
Бинанс: no

Тоесть они прост собирают трохи инфы о фингере, в основном тут важен юа, а стартовый csrf это md5 от нихуя.

Также, чуток посерчил вебархив на предмет рест апишек интересных. В целом, их там есть и они даже работают.

http://web.archive.org/web/20171230040337/https://www.binance.com/login.html

https://web.archive.org/web/20200417073710/https://www.binance.com/en/login

Оно запросило у гитест капчу и тот вернул капчу с картинкой бинанса

Довольно интересно копаться в сурсах через веб архив.

Старая: https://accounts.binance.com/gateway-api/v1/public/authcenter/login

Нынешняя (на момент создания софта): https://accounts.binance.com/bapi/accounts/v2/public/authcenter/login

Нынешняя (на момент написания этой заметки): https://accounts.binance.com/bapi/accounts/v3/public/authcenter/login

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

Тоесть если проходит логин и пароль, но девайс по юа новый, в device_name можно пропихнуть свой текст. Вроде и прикольно, но просто текст, более ничего запихать не удалось, а это до уязвимости не дотягивает(

Итог:

Авториз работает, банан сосатб. Вот такой скриптик по итогу вышел.

Хэшим логпас и пас.

Добавляем девайс дату, как баз64 от нашего фингера

Запрашиваем и решаем капчу

Формируем запросек на рукапчу и дергаем ответ

Ну и собственно шлем запрос авториза, а потом дергаем токен и куку, выполнил от куки хэш.

По итогу чека авториз воркал (на момент написания софта)

Капча slider (geetest)

Я был бы не я, если б не сел чекать и дефолтную капчу. Как я потом понял, оказалось, что эт был geetest. Хотя, выглядело не то чтобы сильно страшно.

Шифрование и данные

Начнем с шифрования.

Имеем тело. Данными о решении будет data.

Напоминаю, урл банана: https://accounts.binance.com/ru/login?return_to=aHR0cHM6Ly93d3cuYmluYW5jZS5jb20vcnU%3D

И так, картинка прилетает 1 куском, получается дальше он ее режет по ширине пазла и высоту не знает, умно)

Хорошо, как обычно, открываем стек вызовов, находим хуйню. Осталось теперь понять как работает эта хуйня, а именно как формируется тело запроса.

Вариантов фона за пазлом не много и это наводит на некоторые мысли по решению самого поиска пазла. А именно, собрать фон без дырки и шукать потом отличие. Также заметим, что из исходного изображения ещё 60 пикселей слева нужно вырезать. 60 чтобы отрезать пазл и ещё 60, ТК в браузере туда ставится пазл. Но об этом трохи позже.

Тэкс. Ну, начало найдено, где бреки ставить более менее ясно.

Чуть потыкав находим ключевой поинт, от которого и будет строиться весь остальной ресерч. Это итоговый жысончег, который мы шифруем.

Вот мы получаем параметр ev.

Выведем нашу инфу в лог до шифрования.

Тыкаем далее и попадаем сюда. Видим дохуя страшную обфускацию сглаживания потока (свичкейсы и порядок действий). Где-то тут оно шифрует наше решение.

И по итогу превращает в нечитаемую кашу, а потом перегон в base64.

Ёпта, ну.. пару строк я и ручками деобфусцирую

for (var d = 0; i[o("JF1c", 218)](d, u[s(1242, "%O$2") + "h"]); d++) p += i[s(3410, "7crc")](on, i[n("S3iz", 2013)](u[o("UlhH", 105) + n("k*^^", 1441)](d), t[r("TKH^", 2165) + c("JF1c", 2489)](i[n("u&1s", 2227)](d, t[n("Lw!2", 3103) + "h"]))));

Хуяк!

p = []; for (var d = 0; d<u.length; d++){ p += String.fromCharCode(u.charCodeAt(d)^t.charCodeAt(d%t.length)) } btoa(p)

Получается, оно просто выполняет xor с неким ключом t ("2p5facdc").

Бля, ну тогда надо понять от куда берется этот ключ и шифрование будет выебано.

Сравниваем data из запроса с той что я заенкодил. Всё как надо)

Теперь осталось понять, от куда берется этот ключ t и это, либо ek, либо salt.

Тыкаем по коду и попадаем сюды, где какая-то математика с ключом ek, но в реверс виде.

var c = ["a", "b", "c", "d", "h", "i", "j", "k", "x", "y"].join("") , l = Math.floor(e.length/t);

var i = t.split("").reverse().join("")

Ну тож изи ручками деобфусцирую. На выходе получаю простой код. На вход ek, на выходе получим ключ. Тоесть тело нашли, шифрование, на удивление, тоже.

В итоговом коде это выглядит вот так

А потом отправляем запрос с нашим решением.

Я тестировал каждый этап работы, в том числе этот. Я брекал решенную в браузере капчу, чтобы запрос не улетел, а потом перекидывал все в бас, шифровал и слал на сервер, чтобы убедиться, что все собрал правильно.

Анализ шифруемых данных

Перейдем к анализу шифруемых данных и начнем собирать ev, be, dist

Напомню, что отталкиваемся вот от сюда.

ev - какая-то мини инфа о браузере, размеры экрана и какая-то странная pre.. выглядит довольно не сложно. А вот в be у нас есть el и th, в котором тоже el..

Остаются: be - движение мыши, dist - ответ. Ну, с dist всё понятно.

Получается, нужно всего лишь понять как формировать be и в целом, че это там происходит, и пол дела сделано будет.

В be у нас есть ec, el, th

Покопавшись в коде, можем найти, что оно собирает инфу по событиям. Если ориентироваться на el в th, видно как оно сейвит нажатие мыши, её движение и отпускание.

click: "cl" mousedown: "md" mouseenter: "me" mousemove: "mm" mouseout: "mo" mouseup: "mu" touchcancel: "tc" touchend: "te" touchmove: "tm" touchstart: "ts"

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

Кажется, что в thumbnailEventList координаты только относительно квадратика.

Направил курсор на нижний левый угол, выдало координаты (0, 38), учитывая что кубик 40 на 40, а 0 в у нас от левого верхнего угла.

"me|39,20" - навел и сработал первый тригер "mm|39,20" - появилась лапка "md|24,20" - клик с зажатием "mm|27,22" - тяну лапку до сюда "me|26,22" - отпускаю лапку

В результате получаем хуйню с данными о перемещении.

"dist":133 - решение

"si":{"w":40,"h":40} - дефолт (це размер дырки)

"el" - другие параметры движения. вот те которых дохера

"ec":{"mm":80,"md":1,"mu":1} - количество событий в массиве а также количество тыков на капчу

Если выкинуть кучу данных это превращается в :
'{"ev":{"wd":35,"im":31,"de":"","brla":35,"prde":"9,35,77,59","pl":"Win32","wiinhe":657,"wiouhe":728},"be":{"ec":{"mm":80,"md":1,"mu":1},"el":[],"th":{"el":[],"si":{"w":40,"h":40}}},"dist":133}'

А если по сути, то вообще
{ev: {…}, be: {…}, dist: 133}

Не так уж на деле и страшно

Получается, что be это прост пропарс результатов
be: { ec: {} el: [] th: {} }
От сюда
r={ eventCount: {}, eventList: [], lastEventTime: 0, thumbnailEventList: [], thumbnailSize: {w: 40, h: 40} }

ec - eventCount

el - eventList

th - {thumbnailEventList, thumbnailSize}

Генерация ev это конечно кек

ev = { brla: s(), de: "", im: s(), pl: pc.platform, prde: prde(), wd: s(), wiinhe: pc.innerHeight, wiouhe: pc.outerHeight }

Что же это за хитрая функция s..
function s(){ (Math.floor(50 * Math.random())*2) + 1; }

На деле, be не такой уж и сложный

be: { ec: { md: 1 mm: 80 mu: 1 } el: [ ] th: { el: [ ] si: { } } }

Где это просто счетчик

ec: { md: 1 mm: 80 mu: 1 }
Большая координат движения мыши el: [ ]

Статика si: { h: 40, w: 40 }
Малая (относительно квадратика) группа движения мыши el: [ ]

Генерация данных

Штош, разобрались с реверсом шифрования и поверхностно с данными, осталось понять, как генерировать эти самые координаты.

Начнем сначала. У нас оно чекает координаты.

Чуть деобфусцируем и получим

var u = e.getBoundingClientRect() , l = Math.round(t.clientX - u.left) , d = Math.round(t.clientY - u.top)

x: Math .floor(l).toString() y: Math .floor(d).toString()

Получается, шо это код, который дергает относительно этого маленького пазла, как было замечено ранее.

По сути просто вот эти значения - старт значения кнопки.

Для генерации времени использует (new Date).getTime()

Напомню, у нас есть несколько типов событий:
click: "cl" mousedown: "md" mouseenter: "me" mousemove: "mm" mouseout: "mo" mouseup: "mu" touchcancel: "tc" touchend: "te" touchmove: "tm" touchstart: "ts"

Фул блок капчи на самом деле содержит много областей. В зависимости от того, куда я навел мышку, кликнул и тд, выводится соответствующее событие.

Пример набора событий.

0: "im|mm|355,284|1648175834425|1" 27: "im|mm|120,385|5|1" 39: "shim|mm|85,397|10|1" 40: "wi|mm|84,398|7|1" 50: "slth|mm|76,416|10489542|1" 51: "wi|mm|282,505|882163|1" 52: "wi|mm|378,413|1994077|1"

Действие над im, тип действия mm - навел на картинку

Действие над shim (пазл), mm - навел на картинку

Хорошо, раз у нас дохуя всяких областей, разберемся какие вообще мы имеем области:

wi - область окна капчи

im - область картинки пазла

shim - область полоски для пазла

sl - область слайдера. скорее всего, за исключением область квадратика и надписи

trtx - область надписи на слайдере

slth - область квадратика слайдера, исключая иконку

ar - область иконки на квадратике слайдера. В идеале, в ней и нужно двигаться при решении.

Имеем области:
wi - область окна капчи im - область картинки пазла shim - область полоски для пазла sl - область слайдера trtx - область надписи на слайдере slth - область квадратика слайдера, исключая иконку ar - область иконки на квадратике слайдера

Имеем события:
click: "cl" mousedown: "md" mouseenter: "me" mousemove: "mm" mouseout: "mo" mouseup: "mu" touchcancel: "tc" touchend: "te" touchmove: "tm" touchstart: "ts"

Может возникнуть резонный вопрос нахуя нужно вот это? А вот в чем фокус:

0: "im|mm|355,284|1648175834425|1" 27: "im|mm|120,385|5|1" 39: "shim|mm|85,397|10|1" 40: "wi|mm|84,398|7|1" 50: "slth|mm|76,416|10489542|1" 51: "wi|mm|282,505|882163|1" 52: "wi|mm|378,413|1994077|1"

0: "|22,2" 1: "mm|22,2"

Ещё раз 50: "slth|mm|76,416|10489542|1"

И 1: "mm|22,2"

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

И тут я прикинул, что закодить это все будет не очень просто. Ну точнее, мне не очень уже хочется так сильно ебаться. И кто-то наверное спросит "а почему бы просто не заюзать windmouse?", а я, который о ней узнал только после того как сделал софт, отвечу:

А потом я покумекал и решил чекнуть "что если решать имитируя телефон?". Должно же быть ну явно по проще. И таки да.

Всего лишь один простенький массив нужно генерить и всё. Еще раз, нам нужен только el для be (большой), а для "th":{"el":[].. уже не нужен. Не то чтобы это прям на сильно проще, но точно не сложнее)

Дергает оно инфу о положении с тачскрина:

clientX: 42.66666793823242 clientY: 509.3333435058594 force: 1 identifier: 0 pageX: 42.66666793823242 pageY: 509.3333435058594 radiusX: 15.333333015441895 radiusY: 15.333333015441895 rotationAngle: 0 screenX: 366 screenY: 520

Да, нам все еще нужно чекать положение пазла относительно всей высоты страницы, но лучше генерить 1 хуйню, чем 2.

Учитывая логи выше, получается, нужно зажимать только иконку стрелки, чтобы мозги себе не ебать. 44х44 - кнопка, 24х24 - иконка.
Тоесть: x = 40+((44-24)/2)+random(0,24)

slth словил на координатах, которые означают левый край, тоесть все оки.
x = 40; y = 510
at словил тыкая тож в край иконки
x = 54; y = 524

at словил тыкая тож в край иконки
x = 52; y = 520

Получается следующее:
Минималка: x = 50; y = 520
Максималка должна быть: x = 50+24=74; y = 520+24=544
Но это в рамках начального положения.

Для теста сложил время начиная с первого значения массива:
"ar|ts|61,526|1648212768794|1"
от 1648212768794, получилось 6419

Вычел от lastEventTime: 1648212775213, получил то же значение
значит я все правильно посчитал. А значит, время действительно правильно посчиталось. Единственное, дало пол секунды хз откуда.

Хорошо, перейдем к итоговому решению. Задаем константы.

Шизо генерация.. Вместо того, чтобы юзать windmouse, которая вероятно и в бас стоит, я наговнял свой алго ветренной мышки.

По итогу генерится вот такая красота.

С другой стороны, блять, оно ведь, действительно, по итогу работало..

Решение пазла

Вернемся немного назад, ведь нужно же еще и само положение ответа как-то найти.

Классически слайдер решают собирая фоновую картинку без дырки и сопоставляют с той что в задаче.

На это я намекал чуть ранее.

Но работает это, когда картинок фона не много, а хотелось бы чет более универсальное.

Также, на форуме бас были 2 старых решения.

В обоих суть сводилась к поиску кусочка пазла на картинке.

Но в этом подходе качество было крайне скверное. Поэтому, как всегда, я решил пойти своим путём.

Общая тактика решения должна свестись к тому, что первым шагом мы подрежем мини пазл и чекнем его положение, далее кропнем картинку по высоте этого пазла, а потом как-то надо будет найти куда его двигать.

Я стал разгонять, а как можно улучшить картинку, убрать всякий мусор, чтоб искало положение лучше, чем бас это делает дефолтно. Погонял разные фильтры:

Перегон в серый

Уменьшение цветов

8 бит
яркость и контраст (30 - 100)

Я пробовал делать перегон в чб по порогу (нативными средствами бас)

Ну и оно вроде как работает, причем довольно неплохо.

Но результат был не достаточно хорош для меня.

В процессе работы также возникла очень странная бага: на картинке нельзя было рисовать через бас.

Скриним, рисуем на картинке и.. ни ху я

Берем картинку в base64, получаем айди, применяем по айди формат в жпег,, получаем base64, получаем опять айди и рисуем прямоугольник.

Я хуй знает почему, но даже на скрине не позволял рисовать. До сих пор не разобрался.

Оператор Собеля

Кароче, шукал я, шукал, и потом я наткнулся на это (Алгоритмы выделения контуров изображений):

Оказалось, что это база в обработке изображений. (Более подробная инфа про фильтр собеля с хорошим кодом есть туть). Тоесть, во всяких спец либах типо OpenCV именно так и решают мою задачу

Прогнал свою картинку на одном из сайтиков и понял, что это именно то что я искал. Онли пазл, ни каких цветов, ни какого мусора.

Детектор границ Кенни (Canny)

Чуть поискав дальше, я нашел, что у этой задачи (выделения краев) есть допил на фильтра собеля, чтоб лучше фильтровать мусор. Это был алгоритм кеннни/кэннни/канни.

Я искал то, что работает без канваса, в 23.2.2 и тут (в canny-edge-detector) всё работает. Это доп либа для библиотеки image-js, либы с кучей фич для обработки изображений.

Даже код понятный.

Очень простой и логичный подход, но на удивление эффективный. Просто переводим картинки в серое, уменьшая тем самым шум от цветов, упрощая картинку, затем применяем фильтр Гауса, чтобы добавить мыла и уменьшить количество деталей, а после восстанавливаем детали фильтром собеля (алгоритмом поиска края). Тем самым мы оставим только важные детали на картинке и получим чб. По итогу мы получим только очертания, без всякого мусора. При этом, размечать ничего тоже не надо. Иные подходы к слайдерам я разберу в одной из следующих статей.

Я накидал простой код..

И в итоге оно сработало!

Поэтому я сразу же решил упаковать все в модуль.

Юзать ноду конечно прикольно, но иметь 1 готовый блок на много прикольнее. Для этого я и сделаль сасный прекрасный модуль

Чуть потыкав код модуля, обнаружил, что в меню можно нарисовать практически что угодно. Гифку, видео.. Ну я и нарисовал.

Хотел поставить эту гифку, но оно сильно увеличивало время загрузки баса..

Чет похерилось качество при конвертации в гифку.

Анимаш очка

И еще..

В итоге остановился на более минималистичной анимации, прост чтобы что-то мелькало.

Че вышло по итогу?!

Дергаем мини пазл, отрезая X по фиксе и Y по прозрачности (сверху вниз).

Докинем контуров, кроп фона и поиск картинки в картинке.

Объединив генерацию мышки и шифрование тела из прошлых разделов с решалкой можем начать тестить.

Ну и все ебашит по красоте. Единственное, в половине случаев ему не нравились движения мышки. Фрод типо. С другой стороны, у меня и руками оно решалось через раз.

99% решения, сука. Не решилась всего одна ебучая картинка.

Преколы

Помимо этого нашел еще всяких приколов. Тогда я открыл для себя huggingface, где увидел кучу нейронок. Оказалось, что детектор края делают и на нейронках (нахуя, не очень понятно, но наверное в этом есть смысл).

Ну и типо это все бесплатно. Тоесть оно решает и гугл и hcaptcha и эт готовые модельки. Как гитхаб, только для нейронок и с демками.

Оказалось, есть алгоритмы генерации всякой хуйни на фракталах.

Вот такую всякую хуйню я почекал. Времени въебал конечно много, но было довольно весело.

Ну и казалось бы фсе, но нет..

Сoinmarketcap

Спустя пару недель, как я разъебал банан, спросили "могу ли я на заказ сделать coinmarketcap (авторизация и регистрация)?". Ну я и решил чекнуть че же там, со стороны выглядит, что банан, что coinmarketcap, примерно как одна хуйня.

Ебать, ну конечно я могу)

А потом я такой: лол они просто капчу банана поставили.. и домен в капче банана.. и подписи банана.. и кучу еще чего как у него. хм.. слишком уж все похоже.

Интересно, конечно, что линка на капчу до сих пор валид: https://public.bnbstatic.com/image/antibot/image/SLIDE/20220804/01/f4b6ab69cf7f42939d9a0c77eacfea29.png

Регистрация

Хоть капча и та же (как и всё остальное), на всякий случай проведем поверхностный ресерч.

И csrf токен в урле, как и в банане, md5 от нихуя

В случае с бананом я прост 1 раз снимал свой фингер и юзал его, но раз это заказ, много аков.. желательно и этот момент по красоте порешать.

Поэтому, нашел все данные для генерации фингера - deviceInfo и его подпись fingerprint. Для полноты картины нехватает только 3х подписей x-se на реге (x-se-bh, x-se-pd, x-se-rd) и Fvideo-Id. Фингер, кстати, подписывается через MurmurHash3 (x64hash128).

Про MurmurHash3 из либы fingerprintjs я писал уже на примере yahoo. Открытые сурсы либы нашел на каком-то гитлаб через гугел.

Штош, раз ясно что собирать, накидаем скриптик.

o = {
screen_resolution: i.screenResolution,
available_screen_resolution: i.avaScreenResolution,
system_version: Rt(),
brand_model: _t(),
system_lang: r.language,
timezone: Mt(),
timezoneOffset: r.timezoneOffset,
user_agent: r.userAgent,
list_plugin: Pt(r),
canvas_code: Bt(r.canvas),
webgl_vendor: n.vendor,
webgl_renderer: n.renderer,
audio: r.audio,
platform: r.platform,
web_timezone: r.timezone,
device_name: Et()
}

Данные тянуть можно из бас фингера, разве что аудио и обычный канвас не получится.. можно сделать нормально их, а можно чисто шума туда въебать, всеравно это хэш, а на фрод оно тут не повлияет)

Ну я и накидал за пару минут.

В отличие от банана, тут было еще некоторое количество дополнительных приколов, например подписи в заголовке, те самые x-se. А x-request-id, как и у банана, это тупо uuid4, а именно - uuid4().replace(/-/g, "")

Ну и.. вот они, осталось деобфусцировать.

Опять тело с хуйней как у капчи, опять xor по ключу. Хотя стоп, че это блять за координаты ебучие. Заметим ключик "abcddcbabadc7hbw", он нам еще пригодится)

Оказалось, оно тречит как чел тыкает вводит форму, это и есть подпись x-se-bh.

Я взял и деобфусцировал ручками эту хуетень, получив код. Ксорим мы как раз с "abcddcbabadc7hbw". Причем, как оказалось, он статичный.

Хорошо, перейдем к следующим подписям

Возьмем подпись x-se-rd. Мы енкодим какую-то рандом строку?!

Отреверсим алгоритм и получим.. такой же код как и для прошлой подписи. И ведь с x-se-pd ровно то же самое.

Заметим, что дополнительно каждая подпись енкодится еще одним алгоритмом

Задекодим его и получим ровно то что ожидали. Однако, статичное число 1100..

Я почекал и оказалось что оно считается из длинны. И тут я подумал, ну а что если я возьму сделаю по шагам енкод, а потом наоборот декод, у нас ведь ксор. И это, действительно, сработало.

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

А актуален ли этот код? Да, перед выпуском данного материала я чекнул coinmarketcap и оно реально до сих пор работает.

x-se-pd и x-se-rd похожи на рандом. Потыкав чуть код (пол часика тыкал код деобфусцируя через консольку), я убедился, что это и есть рандом. Тоесть рандом + контрольная сумма от него. Назвал я этот алгоритм hueta.

Итоговый код. Скорее всего, они они чекают тайм штамп в этой псевдо рандом подписи.

Вернемся к подписи x-se-bh, тк её же надо тоже генерировать..

А шо нам тут собственно надо?
ec - счетчик каждого вида в el el - набор всех движений mt - статик параметры полей sg - хуета хеш от el si - сумма всех элементов el t - текущее время

Ну, с sg всё понятно

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

И тут я подумал, можно же просто взять и сделать ctrl+c ctrl+v, как я люблю. В смысле, имитировать копировать вставить и эт сильно упростит мне задачу.

0 - номер блока ts - тип события 1659865878895 - время старта 128,184 - координаты относительно всего экрана 112,28 - координаты относительно центра объекта

Для события keydown не нужны ни какие координаты, как и для touchend, но тут добавляется всё равно черт очка -

Я проделал все действия и заготовил образцовый шаблон.

Образцовый шаблон:
0: "0ts1659871850308-178,184-163,28" 1: "0te114-," 2: "0k31" 3: "0m9-179,184" 4: "0d1-179,184-163,28" 5: "0u3-179,184" 6: "0k156" 7: "1ts6204-170,286-155,26" 8: "1te98-," 9: "1m9-171,287" 10: "1d2-171,287-155,27" 11: "1u9-171,287" 12: "1k78" 13: "1k257" 14: "2ts1977-186,532-171,32" 15: "2te219-," 16: "2m53-187,532" 17: "2d4-187,532-171,32" 18: "2u5-187,532"

Тыкаем на поле, потом жмем кноп очки копировать вставить, потом отпускаем и еще 1 раз но на второе поле, а потом тыкаем на кнопку отправки и тоже отпускаем.

touchstart touchend keydown mousemove mousedown mouseup keydown ————————- touchstart touchend mousemove mousedown mouseup keydown keydown ————————- touchstart touchend mousemove mousedown mouseup

Я всё это перевел в код

А потом собрал всё в более полный вид. Изи)

Так, с этим разобрались. В итоге, чё по хэдерам?

Пройдемся по порядку в хэдерах запроса реги
URL запроса: https://api.coinmarketcap.com/auth/v4/user/signUp
1) x-csrf-token= о, он приходит в ответ на первый запрос
2) Дефолт хедеры:

  • accept: application/json, text/plain, */*
  • accept-encoding: gzip, deflate, br
  • accept-language: ru-RU,ru;q=0.9
  • cache-control: no-cache
  • content-length: 164
  • content-type: application/json

3) cookie имеют тут Fvideo-Id 33899f3fa49a9da38d94435ff364252d052666b5, но больше там ничего нового или страшного нет, кроме sensorsdata2015jssdkcross..
4) device-info ну эт фингер
5) fvideo-id та самая золупа которую я и буду дальше ковырять
6) x-request-id: 6648b1f9fc3541ddb46540b670cd2b50 - uuid4
7) Дальше подписи, как генерить их уже ясно, осталось данные научиться правильно готовить

Кстати, это тоже uuid4: "X-UI-REQUEST-TRACE", "X-TRACE-ID", "BNC-UUID"

Чекал я Fvideo-Id, но ни где его не было и я уж было начал думать, что это какая-то генерация, но нет, он есть в ответе.

Запросек https://api.commonservice.io/fvideo/tenant/sign/web?en=CXU&t=cmc

А получаем мы его отправив фингер

От фингера deviceInfo оно отличается неймингом и парой доп параметров, в остальном смысол одинаковый. Главное, канвас и аудио хэш совпадают, это наверняка и чекают

До него улетает запрос на странный урл со словом antibot: https://api.commonservice.io/gateway-api/v1/friendly/antibot/coll

Параметр c не base64, потому я попробовал декодировать как подписи до этого. И оно сработало.

Обратим внимание на 52ce8bda-04ee-4fec-bea1-2c2781d992f7.

Тоесть мы сначала шлем https://api.commonservice.io/gateway-api/v1/friendly/antibot/coll и после этого запрашиваем Fvideo-Id, при том передав один и тот же идентификатор. Так как это все на одном домене, закономерно предположить, что это отдельный антифрод.

Тогда антибот тож нужно пилить..

se_ данные эт рандом с таймингом и контрольной суммой, как и в хэдерах ранее.

А вот ev это еще один фингер. Даже парочку страшных слов есть, корс, языки.. ужос.

На самом деле, тут ничего страшного нет, чуть ручками деобфусцировать и готово.

hl - window.history

ivw - ('$cdc_asdjflasutopfhvcZLmcfl_' in document) || !!i['webdriver'] || ![];

ismo - /Android|webOS|iPhone|iPad|iPod|BlackBerry|Mobile/i.test(navigator.userAgent) || 'ontouchstart' in window || ('orientation' in window);

cors - window.XMLHttpRequest

Чё по кукам?
1) sajssdk_2015_cross_new_user=1;
2) sensorsdata2015jssdkcross={"distinct_id":"1827ee10e6213e-07d1158fc471378-5437971-250125-1827ee10e6a12d","first_id":"","props":{"$latest_traffic_source_type":"直接流量","$latest_search_keyword":"未取到值_直接打开","$latest_referrer":""},"identities":"eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTgyN2VlMTBlNjIxM2UtMDdkMTE1OGZjNDcxMzc4LTU0Mzc5NzEtMjUwMTI1LTE4MjdlZTEwZTZhMTJkIn0=","history_login_id":{"name":"","value":""},"$device_id":"1827ee10e6213e-07d1158fc471378-5437971-250125-1827ee10e6a12d"};
3) _ga=GA1.....; _gid=GA1.2.1...;
4) se_sd=gUKVhWRwDRVGQ4HxaCBlgZZF1AgkNESW1tWBcVEN1hdWwVlNXVIO1; se_gd=hNTVhUhgRHAUVBVlSCgggZZXQElUGBVUFoSBcVEN1hdWwDlNXVcM1; se_gsd=XjEgOzBhLCM0CQUxNAw2Ci0xEgFXAQBXV1lBW1NWVlhbAlNS1;

sensorsdata2015jssdkcross

Вообще, это вроде какие-то чисто китайские кукисы.

А вот se_.. непонятно только от куда берется se_gsd

se_gsd берется из ответа на этот запрос + немного доп обработки.

"T1LVF1UUlBUAlNwAgwYBQMIUVpkIDM2IQMxDjE3VgMhFSdyLDA0w9223q9dj04".split('').reverse().join('')

parseInt("40jd9q3229w0ADLydSFhMgV3EjDxMQI2MDIkpVUIMQBYwgAwNlAUBlUU1FVL1T".length/0x5)

А такая функция у меня уже есть

Еще встретил в процессе реверса какие-то преколы.

Как я сделал по итогу

Мне было лен парсить и форматировать фингеры баса, поэтому я сделал html страничку и прогнал на эмуле скрипты с установкой фингера.

Ну а далее все как было ранее, формируем фингер и докидываем хэш, формируем из него фингер для fvideo, дергаем кукисы, касуем генерацию sensorsdata2015jssdkcross, формируем sd_ подписи и проходим antibot, получив fvideo.

А далее автори. Кастуем подписи, получаем куки, решаем капчу, пытаемся войти по токену качпчи.

С капчей все как было разобрано ранее. Получаем, режем, решаем, получаем токен решения.

В итоге, все работает. Не удивительно.

И для невалид пароля.
И для валид.

Единственное, модуль на 23.2.2 бас в 12 ноде перестал работать, тк тянул новую версию image-js с какой-то багой. Хотя на новом бас в 18 ноде оно работает нормально. Для 23.2.2 я нашел ласт валидную версию - 0.21.9 и фиксанул модуль.

Авторизация

И тут меня ждал сюрприз. Я делал под слайдер, а тут не слайдер. Тут хуйня как у бинанса. Нахуя я тогда вообще ебался со слайдером?

Я начал тыкать капчу, презагружал много раз, переключал на регу. И случилось чудо. Я обнаружил "фичу" с подменой капчи)

Цимес тут вот в чем. Нельзя просто так взять и получить капчу. Капча привязана к "приложению", а именно токену который оно возвращает. Тоесть при запросе авториза оно вертает securityId (securityCheckResponseValidateId)

По этому айди вертает только капчу приложения, тоесть что бы я не написал biz mode (CMC_register например), оно вернет такую капчу, какая положена для этого ендпоинта.

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

Собственно, это я и сделал, запилив по итогу фул рабочий софт всего с 1 капчей.

Все ебашит по красоте)

А почему не соло?

Потому-что у меня есть дохуя потоков чтобы выебать бинанс)

Дополнение

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

И так, у нас есть тело

Чекаем вызовы

Чуть выше видим формирование тела решения капчи

Перейдем тогда сразу к шифрованию. Видим ультра гига мега шифровку на ксор в пару строк)

У меня по итогу ручной деобфускации вышел тот же код по сути.

В мое случае также sig была в качестве контрольной суммы, но туть она прилетает сразу.

Чекнем ev. Штош, без обфускации выглядит совсем не страшно

Ну, а c be тут все тоже на много яснее.

Я искал файл который дебажил (с обфускацией), но он не сохранился, благо на скрине осталось название, а потому я смог дернуть искомый файл)

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

А это значит, что можно подсунуть её токен в любой эндпоинт)