История очень похожа на взлом Егора, за исключением того, что это не совсем рэйс, вернее, совсем не 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.А затем сравнивается с числом T2, которое передается извне и меняется нами на число с плавающей точкой (допустим float) 180.01.
Если сравнение T1 с T2 происходит целочисленно (в int) то они равны, если как float (что случается если не уследить в некоторых алгоритмах или системах или явно не указать) — то они разные!
После сравнения — токен T2 легальный, и что не менее важно токен T2 не был еще использован — происходит увеличение остатка денег (платеж состоялся), а T2 переписывает T1 (токен использован), при этом он обрезается до целого если нет precision overflow. т.е. снова 180. И стандартным уже рэйс все повторяется снова и снова. Пока токен не устареет ну или пока время не выйдет…int(180) = int(180.01) float(180) != float(180.01)
Постоянное же увеличение 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] Тут моралисты понабежали, ну типа «Они вас не просили проверять их сайт. Давайте я вам тоже чего-нибудь хорошее сделаю, и денег попрошу» или это «Вымогательство» и т.д.
Отвечу сразу всем остальным желающим постебаться за мою совесть:
- давайте я вам тогда денег не дам (да имею права), но при этом скажу — «это что-нибудь хорошее, однозначно оставь все-равно, без денег и я на тебя все равно в суд подам».
- мое личное отношение к «клиенту» будет зависеть от него самого, как минимум от тона, каким это будет сказано.
- я потратил кучу времени на написание поста, и не имею ни малейшего желания выслушивать нытье очередного «тролля» по поводу моих моральных качеств, за которые могут говорить только люди, действительно меня знающие.
- сайты
например «про кошечек»социальных проектов или из оперы «опенсорс» всегда обслуживаются вне очереди, совершенно с другим отношением и абсолютно бесплатно.
Но не люди зарабатывающие деньги, при этом экономящие их на своих клиентах и их безопасности. Особенно если они мне угрожают или хамят. - Ресерчинг — это мое хобби, позвольте мне его осуществлять, самостоятельно оценивая моральные стороны вопроса. Я на этом не зарабатываю и даже обычно не прирабатываю. Сам я считаю себя законопослушным гражданином, и таким и останусь, мнение же троллей об этой стороне вопроса не влияет на это ни в коей мере.
Желание поделится с людьми «знанием» и время потраченное на пост пока пересиливают желание убрать к чертям этот пост в черновики, но баланс однозначно нарушен.
Конструктив и мнения по теме статьи, т.е. обсуждение технической стороны вопроса выслушаю с огромным удовольствием (а будет свободная минутка и отвечу).

