Тема провисела на форуме 2 суток, набрала 500 просмотров и 0 ответов. Куда подевались все гуру-хакеры?
Ладно, попробую задать более конкретный вопрос.
Что имеем:
Последние несколько дней я пытаюсь сделать игре SQL-инъекцию.
Как я уже говорил, клиент обменивается с сервером HTTPS запросами.
На сервер отправляются обыкновенные POST-запросы вида
https://www.game.ru/public/api/scrip...ta1&var2=data2...
В ответ приходит целая гора данных в формате JSON, основываясь на которых, клиент выдает картинку игровых действий (передвижение, битвы, игровой инвентарь ит.д.)
Разумеется никаких html-страниц и форм нет и в помине. Но поскольку я могу слушать трафик напрямую, то и вмешиваться в него могу тоже напрямую. Следовательно - изменять переменные отправляемые на сервер так, как если бы я это делал в строке запроса GET.
Я могу придать переменной любое значение, однако часть символов экранируется.
А именно символы / (слэш), \ (обратный слэш) и "(двойная кавычка).
Все остальные символы поступают на сервер без экранирования. По крайней мере символы ['],[`],[*],[:],[;],[&],[%],[$],[@],[?],[(],[)],[{],[}],[,],[.],[!],[-],[|],[=],[^],[#] можно передать в исходном виде.
Однако SQL-инъекцию затрудняет то, что часть переменных защищена проверками на валидность, а другая часть, по видимому, в SQL-запросах не участвует. Но я таки отыскал одну переменную, через которую инъекция возможна. Как не удивительно, но это переменная
uid (id игрока).
Что работает:
Предположим мы работаем с исходной переменной
uid=99
Если привести ее к виду
uid=99' получим ошибку 500 (Internal Server Error), т.е. ошибочный SQL-запрос который сервер выполнять откажется.
Значение
uid=99/* передать к сожалению нельзя из-за экранирования слэша.
Значение
uid=99-- будет обработано.
Так же будет обработано:
uid=99--+любой текст
Будет обработано:
uid=99+and+null--
Будет обработано:
99+and+null
Будет обработано:
99+and+concat('1','2')
Будет обработано:
99+and+version()
Будет обработано:
99+and+@@version
Приведет к небольшому зависанию и ошибке 504 Gateway Time-out:
uid=99+or+1--
Ну еще бы, аккаунтов в игре зарегистрировано более 13 млн.
Вот такая переменная затянет выполнение запроса на ~2300мс:
uid=99+and+BENCHMARK(10000000,md5(current_date))
Обычно запрос выполняется за 5-15мс.
Скорее всего есть возможность провести DOS атаку, но меня это не интересует.
Что не работает (подводные камни):
Проблема 1.
Первая проблема состоит в том, что значение переменной uid используется не только в запросе к БД, но и каким-то образом связано с проверкой валидности игровой сессии. Всего в проверке сессии их учавствует три -
uid (номер игрока),
serverid (номер сервера) и
sessionToken (ключ сессии).
uid - всегда неизменный для отдельного игрока
serverid - неизменен до тех пор пока мы играем на одном и том же сервере
sessionToken -выдается каждый раз новый. Происходит это при входе в игру, либо при смене сервера. Предыдущий sessionToken при выдаче нового становится недействительным. Внешне он напоминает хеш md5.
Если поменять хотя бы один символ в любой из этих переменных, получим обрабатываемую игровым движком ошибку
"Ключ сессии неверный!". При этом, несмотря на то, что запрос к БД будет выполнен, обычный набор данных в JSON-формате PHP-скрипт нам не отправит. А если он их не отправит, то и результаты SQL-запроса вставлены никуда не будут...
То есть не представляется возможным получить какую-либо пользу даже от успешного внедрения в запрос конструкции UNION SELECT...
Как решить данную проблему я не понимаю и очень рассчитываю на вашу помощь.
Проблема 2.
Собственно, мне не удается узнать число столбцов между SELECT и FROM.
Я пытался это сделать вот так:
uid=99+union+select+null
uid=99+union+select+null,null
uid=99+union+select+null,null,null
...
и т.д.
К сожалению сервер все время возвращает ошибку 500. То есть запрос не выполняется. Предположим это может быть просто результатом неверного числа добавленный мной null. Я утомился после 30-го по счету. Конечно, их может быть 100 или 300. Ну да черт с ним. В случае чего я пару часов своего времени на перебор убить могу. Но что если сервер отказывается принимать саму конструкцию
select+null? Или оператор
union? Или еще что-нибудь в синтаксисе моего запроса?
У меня есть основания полагать что это так, поскольку переменная
uid=99; (добавлена точка с запятой) сервером обрабатывается. В то время как
uid=99;+select+null приводит к ошибке 500, хотя после точки с запятой количество нуль-значений в запросе никак не должно влиять на его валидность. Ведь мы не объединяем результат с предыдущим, а делаем новый независимый запрос.
Почему конструкция
uid=99;+select+null не работает?
Я не знаю какая именно СУБД там находится. Но поскольку сервер на CentOS думаю я не ошибусь если предположу что это MySQL, причем с версией никак не ниже 4.* Мне точно известно что Apache имеет версию 2.2.15, а PHP версию 5.4.11. По-моему крайне маловероятно, что вместе с ними стали устанавливать бы такую древность как MySQL 3.* А современные версии MySQL поддерживают, как UNION, так и расщепление запросов оператором ; (точка с запятой).
Следовательно, существует какая-то другая причина, по которой
uid=99;+select+null не работает. Но какая?
Наверно ответ на вопрос элементарный. Но я к сожалению ничего не смог нагуглить, а чтобы догадаться самому - не хватает опыта.
Требуется ваша помощь.