Pull to refresh

Как я однажды взломал онлайн-казино

Reading time 7 min
Views 126K
Вдохновившись рассказом 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] Тут моралисты понабежали, ну типа «Они вас не просили проверять их сайт. Давайте я вам тоже чего-нибудь хорошее сделаю, и денег попрошу» или это «Вымогательство» и т.д.
Отвечу сразу всем остальным желающим постебаться за мою совесть:
Скрытый текст
  • давайте я вам тогда денег не дам (да имею права), но при этом скажу — «это что-нибудь хорошее, однозначно оставь все-равно, без денег и я на тебя все равно в суд подам».
  • мое личное отношение к «клиенту» будет зависеть от него самого, как минимум от тона, каким это будет сказано.
  • я потратил кучу времени на написание поста, и не имею ни малейшего желания выслушивать нытье очередного «тролля» по поводу моих моральных качеств, за которые могут говорить только люди, действительно меня знающие.
  • сайты например «про кошечек» социальных проектов или из оперы «опенсорс» всегда обслуживаются вне очереди, совершенно с другим отношением и абсолютно бесплатно.
    Но не люди зарабатывающие деньги, при этом экономящие их на своих клиентах и их безопасности. Особенно если они мне угрожают или хамят.
  • Ресерчинг — это мое хобби, позвольте мне его осуществлять, самостоятельно оценивая моральные стороны вопроса. Я на этом не зарабатываю и даже обычно не прирабатываю. Сам я считаю себя законопослушным гражданином, и таким и останусь, мнение же троллей об этой стороне вопроса не влияет на это ни в коей мере.
Т.к. банить я в своем посте на хабре не могу, попрошу просто по человечески: оставьте свое мнение о моих моральных качествах пожалуйста при себе. Вы сэкономите и свое и мое время.
Желание поделится с людьми «знанием» и время потраченное на пост пока пересиливают желание убрать к чертям этот пост в черновики, но баланс однозначно нарушен.
Конструктив и мнения по теме статьи, т.е. обсуждение технической стороны вопроса выслушаю с огромным удовольствием (а будет свободная минутка и отвечу).
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+88
Comments 72
Comments Comments 72

Articles