Вдохновившись рассказом Chikey о том, как он вновь «сломал интернет» Егор прекрати уже ломать все подряд, займись делом каким-нибудь, решил поведать об одной истории с довольно известным за рекой онлайн-казино. Имя этой «организации» не называю, т.к. процентов на 50 уверен, что или совсем не пофиксили, или сделали кривее, чем было до этого.

История очень похожа на взлом Егора, за исключением того, что это не совсем рэйс, вернее, совсем не race condition в чистом его виде. Как оно будет полностью не знаю, я больше практик, чем теоретик. Назовем его «conditional race condition» — хоть и масло масляное, но суть отражает верно.

Как-то вечером, домашние уснули, по ящику одна муть, наши опять проигрывают делать было особенно нечего, на опенсорс решил на сегодня забить, захотелось чего-нибудь сломать. А что ломать (когда Егор уже все сломал), как не банк какой-нибудь или казино (эго, необходимость иногда почувствовать себя крутым парнем, все дела в общем). Это было одно из первых онлайн-казино, которое мне тогда приглянулось в поиске.

Не секрет, что экономят на программистах, тестировщиках и т.д. все или почти все. Я делаю временами аудиты, да и по роду деятельности такого иногда насмотришься, что волосы дыбом. Но тут-то казино! С возможностью вывода (выигранных) вечнозеленых и т.д. Т.е. контора должна вроде соответствовать.

Завел себе аккаунт, и поехали…

Собственно взлом


Как оказалось — здесь тоже сэкономили изрядно.

Сначала решил набросить себе немного «виртуальных» денег. Виртуальная валюта там не выводится (чисто поиграть — потренироваться). Можно было через paypal (10ct = 10V$), можно раз в день через капчу (20V$).

При беглом просмотре страницы «покупки» (загрузив ее несколько раз) обнаружил там токен, привязанный ко времени (заметен инкремент по модулю 180), отправляющийся с капчей после нажатия «Order». Вероятно в базу для моего аккаунта писался еще и datetime времени «покупки» (чтоб значит только раз в сутки), но нам оно сейчас без надобности.

Я хотел сначала попробовать чистый race (я не настолько глуп чтобы поверить про 3-и минуты, ну или 1.5 минуты если используют формулу со смещением), но у меня были сомнения, что нет даже нескольких миллисекунд, т.е. с высокой долей вероятности этот токен скорее всего тоже помечался как использованный в DB, чтобы значит совсем «до завтра» (кстати, так оно и оказалось впоследствии).

В результате решил начать пробовать сразу со своего «условного рэйс». Предположим, что токен пишется в базу int-ом (типа unixtime — т.е. целым числом), ведь они исходят из того, что следующий токен выдадут только через 3 минуты (хотя обычно оно все-таки с каким либо смещением, чтобы даже на границе срок действия его уже истек). Т.е. думаю имеем что-то вида:

create table user_order (usrid int, token bigint, lasttoken bigint, lastorder datetime, ...)

Тут немного техники: я не люблю штуки вида Greasemonkey и ко, хоть и юзаю иногда. Но не люблю (мало что ли таких как Егор). Много можно сделать и из dev консоли firefox-а, но так, по мелочи. Я так тоже довольно редко «работаю». У меня есть собственный (ну почти) плагин, разворачивающий «полноценный» интерпретор тикля (tcl) с api к javascript текущего окна. Типа greasemonkey, только на тикле. Удобно жуть.

В общем, тикль-джиэсный скрипт для «взлома» выглядел следующим образом:

set tok [js form.ordertoken.value]
set i 0
set cntr 0
time {
  ## чтобы не плодить формы - делаем максимум пять штук:
  elm duplicate -overwrite form[expr $i % 5] form
  ## делаем токен "float" и сабмитим форму в таргет (0-4) ...
  js {
    var f = form[expr $i % 5];
    f.target = '_tmp_win_[expr $i % 5]';
    f.ordertoken.value = f.ordertoken.value + '.' + '[format %05d [incr cntr]]';
    f.submit();
  }
  ## отработать события, контекст свич и задержка...
  update; after 5
  incr i
} 10

Т.е. для каждой новой сдублированной формы делаем значение ordertoken в виде числа с плавающей точкой.
Имеем следующее в javascript:

   ...
   var f = form0;
   f.target = '_tmp_win_0';
   f.ordertoken.value = f.ordertoken.value + '.' + '00001';
   f.submit();
   ...
   var f = form1;
   f.target = '_tmp_win_1';
   f.ordertoken.value = f.ordertoken.value + '.' + '00002';
   f.submit();
   ...

Догадливый читатель уже, наверное, понял что происходит.

Следующий SQL-statement будет легально выполнен, если имеем автоконвертирование и float режется до (big)int без ошибки переполнения, а условие будет положительное (т.к. сравнение будет производится во float):

UPDATE user_order set lasttoken = 1432254060.00007, lastorder = now(), vmoney = vmoney + 20
WHERE ... AND lasttoken != 1432254060.00007 AND ...

Что там они проверяли, affected rows или просто if begin… end в транзакции, и SQL ли это вообще, я не в курсе но в очередной раз исполнив скрипт, я стал таки «виртуальным» миллионером! Ну то есть time casino-virt-order 50000.
Пояснение под спойлером
Грубо говоря, где-то сохраняется целое число T1 (как int или bigint) например 180.
А затем сравнивается с числом T2, которое передается извне и меняется нами на число с плавающей точкой (допустим float) 180.01.
Если сравнение T1 с T2 происходит целочисленно (в int) то они равны, если как float (что случается если не уследить в некоторых алгоритмах или системах или явно не указать) — то они разные!
int(180) = int(180.01)
float(180) != float(180.01)
После сравнения — токен T2 легальный, и что не менее важно токен T2 не был еще использован — происходит увеличение остатка денег (платеж состоялся), а T2 переписывает T1 (токен использован), при этом он обрезается до целого если нет precision overflow. т.е. снова 180. И стандартным уже рэйс все повторяется снова и снова. Пока токен не устареет ну или пока время не выйдет…
Постоянное же увеличение float-токена (как у меня в скрипте), позволяет «предотвратить» ситуацию, когда токен хранится все-таки не целочисленно, но проверка его в формуле «легальности» токена осуществляется все-же целочисленно, а проверка «токен изпользован» по какой-либо причине как float.

Как вы думаете, как долго сайт продержался с реальным платежом?! День (да и то потому, что уже баиньки хотелось очень).
Там, конечно, не было time casino-real-order 50000, да и думаю, что такое они заметили бы на раз-два. Ну какой-то же мониторинг средств (читерства и т.д.) должен все-таки быть. Хоть и с десятью тысячами клиентов. Но 50 по 20 я таки исполнил разок (т.е. заплатив «всего» 20 вечнорастущих, т.к. на тот момент это была минимально-возможная оплата).

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

Мораль


Тут вроде и обсуждать нечего. Единственное, что могу добавить — чистый race condition там не работал, я проверял (второй параллельный вызов выдавал «уже использован»). Но судя по «профессионализму» ребят — это была скорее защита от дурака (типа F5 после реквеста и т.д.).

Интересен другой момент, когда токен устаревал (+ 180), оно уже не работало по причине проверки уже даты последнего «платежа» типа lastorder < now() + '1 day'. Но почему-то не с «тем же самым» токеном. Думаю, что болячка та не одна была.
Хотя стоит добавить, что токен совершенно честно помечался как использованный (т. е. transaction с table lock или уровень изоляции типа serializable). Новый токен самому сгенерировать тоже не представляется возможным (вероятно должен быть в их базе для аккаунта). А может как раз выдача нового токена зависела от lastorder.

И да, капча видимо была привязана таким же образом к токену + чего-то еще в форме. Или для нее работал «race», оно мне без надобности было искать.

Кстати, все остальное, от SQL-Injection до разных трюков с известными мне эксплойтами на той платформе выглядели довольно солидно, но как-то все-таки полупрофессионально что ли, т. е. придраться все же было к чему (некоторые вещи не делаются так уже лет дцать как).

Да, еще кое-что: это же казино, т.е. человек не стесненный средствами имеет какое-никакое преимущество перед соперниками, при прочих равных. Как минимум может позволить себе чаще рисковать. В общем, если бы я там играл, мог бы и «почти» легально выигрывать кучу денег у других клиентов этого казино, «обманывая» при этом саму компанию на гораздо меньшую сумму. Плюс были бы затронуты как минимум интересы клиентов этой площадки по отбору средств у населения. Я не думаю, что казино этого не понимало, когда пытались разобраться со мной.

«Разборки» со мной — злодейским хакером


Даже рассказывать не хочу.

Я, по своему обыкновению, сперва выложил им только результаты «эксплойта» — т.е. вот аккаунт, на нем виртуальный мильён денег. Аккаунт создан позавчера, ну и намек — мол расскажу что да как после доната. Про взлом с реальными деньгами я сначала вообще умолчал (другой аккаунт — другой донат). Я не беден, но любая работа должна быть оплачена. Естественно «покаялся», ну т.е. «чисто из спортивного интереса» и «естественно никогда (зуб даю) не буду применять».

Результат:
  • реальная попытка сделать из меня злостного вскрывателя казинов;
  • через некоторое время уже «просьбы» сказать «как» (т.е. о чудо, они даже по accesslog не нашли!). Просьбы чередуются с угрозами (незамедлительно предоставить все, а мы подумаем что с тобой делать).
  • после совсем откровенного намека «работа должна быть оплачена», попытка сделать меня шантажистом (я уже друга адвоката думал подключать);
  • в результате вежливо послал их подальше, снова обязавшись не использовать во вред и игнорируя в дальнейшем все попытки тупого «прессинга».

Что смешно, после закрытия аккаунта уже с реальными деньгами (предварительно вывел только свои 20 евро) в течении некоторого времени еще получал от них письма, мол, «заберите деньги»… Хотел сперва им ответить — потратьте на благотворительность, но они все же не мои. Так и лежат они, вероятно, там…

Люди, не экономьте все же на профи. Мы, профи, стоим тех денег, которые нам платят.

[UPD#1] Тут моралисты понабежали, ну типа «Они вас не просили проверять их сайт. Давайте я вам тоже чего-нибудь хорошее сделаю, и денег попрошу» или это «Вымогательство» и т.д.
Отвечу сразу всем остальным желающим постебаться за мою совесть:
Скрытый текст
  • давайте я вам тогда денег не дам (да имею права), но при этом скажу — «это что-нибудь хорошее, однозначно оставь все-равно, без денег и я на тебя все равно в суд подам».
  • мое личное отношение к «клиенту» будет зависеть от него самого, как минимум от тона, каким это будет сказано.
  • я потратил кучу времени на написание поста, и не имею ни малейшего желания выслушивать нытье очередного «тролля» по поводу моих моральных качеств, за которые могут говорить только люди, действительно меня знающие.
  • сайты например «про кошечек» социальных проектов или из оперы «опенсорс» всегда обслуживаются вне очереди, совершенно с другим отношением и абсолютно бесплатно.
    Но не люди зарабатывающие деньги, при этом экономящие их на своих клиентах и их безопасности. Особенно если они мне угрожают или хамят.
  • Ресерчинг — это мое хобби, позвольте мне его осуществлять, самостоятельно оценивая моральные стороны вопроса. Я на этом не зарабатываю и даже обычно не прирабатываю. Сам я считаю себя законопослушным гражданином, и таким и останусь, мнение же троллей об этой стороне вопроса не влияет на это ни в коей мере.
Т.к. банить я в своем посте на хабре не могу, попрошу просто по человечески: оставьте свое мнение о моих моральных качествах пожалуйста при себе. Вы сэкономите и свое и мое время.
Желание поделится с людьми «знанием» и время потраченное на пост пока пересиливают желание убрать к чертям этот пост в черновики, но баланс однозначно нарушен.
Конструктив и мнения по теме статьи, т.е. обсуждение технической стороны вопроса выслушаю с огромным удовольствием (а будет свободная минутка и отвечу).

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


  1. OlegTar
    22.05.2015 15:52

    sebres если что, посту поставил +


    1. sebres Автор
      22.05.2015 16:18
      -3

      И за это вам респект и спасибо с уважухой пополам.
      Еще бы вы всякие «сильноконструктивных» ветки не создавали — молился бы за вас:)
      если что, я про ту что выше


  1. ChieF_Of_ReD
    22.05.2015 16:53

    Дилетант мимо проходящий
    Т.е. схема вашей работы примерна такова: имея опыт и знания аспектов защиты и уязвимостей, вы перебираете подходы к тем или иным продуктам, нацеливаясь на максимальную «выгоду», которую можно получить.
    Поясню сразу, если казино то это соответсвенно деньги, кинотеатр, бесплатное бронирование билетов и так далее. Соответственно, если бы не получались схема описанная в статье, то читали бы мы просто про немного другое описание «как». Я правильно понимаю?

    По поводу морали, раз уж сегодня это самая интересная тема: Любой труд(!) должен быть оплачен.


    1. sebres Автор
      22.05.2015 17:06
      +1

      нацеливаясь на максимальную «выгоду»
      Нет вы не правильно понимаете, я очень не беден, мне оно и не нужно как-бы (и зарабатываю я другим). Объяснить бы еще это моей дражайшей половинке — Ага! Снова за компьютером на ночь засел. Это раз.

      Это мое хобби — т.е. там где интересно, а не где максимальная выгода. Это два.

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


      1. ChieF_Of_ReD
        22.05.2015 17:17
        +1

        Нет вы не правильно понимаете, я очень не беден, мне оно и не нужно как-бы (и зарабатываю я другим). Объяснить бы еще это моей дражайшей половинке — Ага! Снова за компьютером на ночь засел. Это раз.

        Видать я не совсем корректно выразился: я подразумевал, цель ставится, если смотреть на проблему со стороны, если бы вы были злостным, аморальным «кулхацком», который хочет получить больше денег.

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

        А это уже диалог, «Блин, спасибо большое, я за твою работу хочу дать денег.» А потом уж тебе решать, соглашаться или нет. Даже, если ты ответишь «нет», как минимум человеческое «спасибо» быть должно всегда.


  1. OlegTar
    22.05.2015 16:56
    +4

    sebres
    А теперь по теме поста, я правильно понимаю, что это связано с 0.1 + 0.2 != 0.3?


    1. sebres Автор
      22.05.2015 17:22
      +7

      Ну наконец-то…
      Ответ: Не совсем…
      Проще говоря, где-то сохраняется целое число T1 (скажем как int или bigint) например 180.
      А затем сравнивается с числом T2, которое передается извне и меняется нами на число с плавающей точкой (допустим float) 180.01.
      Если сравнение T1 с T2 происходит целочисленно (в int) то они равны, если как float (что случается если не уследить в некоторых алгоритмах или системах или явно не указать) — то они разные!

      int(180) = int(180.01)
      float(180) != float(180.01)
      

      После сравнения — токен T2 легальный, и что не менее важно токен T2 не был еще использован — происходит увеличения остатка денег и оно инкрементируется, а T2 переписывает T1, но обрезается до целого если нет precision overflow. т.е. снова 180. И стандартным уже рэйс все повторяется снова и снова. Пока токен не устареет ну или пока время не выйдет…


      1. OlegTar
        22.05.2015 17:34

        Спасибо! Понял.


  1. olku
    22.05.2015 22:29
    +1

    Не устану повторять романтикам в «белых шапках», что тест информационной системы проводится лишь с разрешения ее владельца. В большинстве юрисдикций данное деяние — уголовное, а «расскажу что да как после доната» позволяет повесить любые убытки. Ну, зато, пересечении границ больше не будет скучным. :)


    1. nikitasius
      22.05.2015 23:18
      +4

      Проводите тест не напрямую и так же ведите переписку.
      Я поддерживаю автора — если вы нашли брешь в коммерческом проекте, то вы можете или продать ее 3м лицам или самим разработчикам.
      Почему продать? Потому, что они платят своим программистам за работу, и если вы улучшаете их проект, то вы должны быть вознаграждены.
      Главное правило — не входить в долгие дискуссии. Ответ должен быть максимально быстрым.


    1. NSA
      26.05.2015 13:32
      +1

      Да какой тест? Просто мы так её используем. Текаешь ссылки, тут же видишь, каки запросы отправляются, начинаешь ковырять формы, не успел опомниться — а уже дыру какую-нибудь нашёл. Это как поиск ошибок в тексте — их видишь без какой-либо специальной процедуры анализа. Просто читаешь текст (используешь его по назначению) и видишь ошибки.


  1. NSA
    26.05.2015 13:17
    +1

    Сообщать владельцу сайта/серивса о баге? Да ни за что! Если только он знакомый. В остальных случаях будут проблемы. Ну, или сервис заранее сообщает о положительном отношении к таким репортам.


  1. battrack
    26.05.2015 16:19
    +3

    Странно, что многие пытаются осуждать автора поста. Какое «вымогательство», как «шантаж»? Он просто предложил услугу за деньги. Им услуга не понадобилась. Он не стал ее оказывать. Где тут вымогательство? То что попросил денег за информацию об уязвимости? В этом нет ничего аморального. В наше время информация стоит денег. И даже если человек делал это для удовольствия и в качестве хобби, то ничего ему не мешает попросить деньги за в итоге выполненную работу. Художники тоже очень часто рисуют для удовольствия, а не только по заказам. И очень часто у них во время рисования для удовольствия получаются хорошие произведения, которые они потом выставляют на продажу. И никто их потом не осуждает, что они не отдали картину бесплатно, а повели себя «аморально» и решили продать плод своих трудов. Тут такая же ситуация — человек развлекался не причиняя никому вреда. В итоге создал «нечто», что оказалось возможным продать. И не просто продать, да еще и оказать хорошую услугу. Но в итоге получил угрозы и наезды. И кто тут себя аморально вел? Покупать они не захотели, окей — он и не настаивал, все остались при своих. «Никто не просил искать уязвимость?» — ок, ваше дело, мое дело предложить. Если бы отдал им решение за бесплатно — был бы вообще супер бескорыстный человек, можно только восхищаться. Но он решил не отдавать решение бесплатно — это его решение и за это вряд ли можно осуждать.


    1. sebres Автор
      26.05.2015 17:14
      +3

      • конечно, спасибо за поддержку, но...
      • просьба не разводить холивара на «пустом» месте относилась не только к «моралистам» («автор — вымогатель и шантажист»), но и к другой стороне, т.е. к людям, как вы, считающим что я возможно прав...
      • давайте уже закроем эту тему, ибо надоело, т.к.:
        1. цимес статьи как-бы совершенно не в этом;
        2. не считаю нужным снова и снова комментировать моральную оценку моих действий (данную как положительно, так и отрицательно);
        3. как для юридических вопросов так и для нравственных изысканий наверняка существуют другие площадки, другие хабы, или как минимум другие посты;
        4. я совсем не против, если кто-нибудь из моралистов сделает свой пост или опрос на эту тему, в котором это будет обсуждаться хоть до второго пришествия. Более того с удовольствием сам бы перенес некоторые ветки отсюда в такой новый пост, если бы хабр такое умел — позволял