Loading console...
Preparing execution environment... OK
Loading Bash interpreter...
user@HackerWars:~/0day $ ls -a
. .. 0day.log MoonLight.log SkyBridge.log VIP_Zone.log
user@HackerWars:~/0day $ cat VIP_Zone.log
█████████ > В этом логе я расскажу о применении 0day уязвимости 🌄VIP_Zone. Содержание:
[README.txt] Общая информация
[01_01_2021-23_30.conf] Rich Client
[02_01_2021-22_00.conf] Seed of Life
[03_01_2021-20_30.conf] Security
[04_01_2021-19_00.conf] Cashback
[05_01_2021-17_30.conf] Line of Success
[06_01_2021-16_00.conf] Line of Success 2
[07_01_2021-14_30.conf] Money Time
[H4rdF0rk_R3c0v3rY.conf] Атака на сервер H4rdF0rk
█████████ > Вход в сеть подпольных казино 🔱NetKings расположен по адресу https://netkings.hackerwars.ru/. Для входа нужен идентификационный токен (в нашем случае - HackerWars/me) и инвайт-код.
Токен получить легко, а вот инвайты нужно будет поискать...
█████████ > Эксплойт для 0day уязвимости 🌄VIP_Zone был создан.
🌄VIP_Zone > Script activated.
 _    __________    _____                  
| |  / /  _/ __ \  /__  /  ____  ____  ___ 
| | / // // /_/ /    / /  / __ \/ __ \/ _ \
| |/ // // ____/    / /__/ /_/ / / / /  __/
|___/___/_/   _____/____/\____/_/ /_/\___/ 
             /____/                         
🌄VIP_Zone > Target: https://netkings.hackerwars.ru/... OK
🌄VIP_Zone > Credentials found: 01_01_2021-23_30.conf
█████████ > После подключения нас встречает рулетка, в которой нужно угадать цвет. Первым 50 побед подряд набрал 💠❄W4rezM1nder (24).
💠❄W4rezM1nder (24) > Меня насторожила фраза приветствия про "выполнение у вас на компьютере" - сразу полез в js скрипты. Дальше посмотрел, что там твориться и заменил функцию getRandomInt на выдачу нужного мне числа (я предполагал, что это будет номером поля). Мне повезло и данные изменения сработали. Спустя время устал от ожидания "прокрутки" и убрал анимацию.
█████████ > Действительно, результат прокрутки рулетки определяется в JavaScript на самой странице, а значит можно спокойно менять результат.
█████████ > На самом деле, сервер вообще не проверяет, что происходит на стороне клиента. Посмотрим на взаимодействие между ними:
Запрос от клиента:
{bet: "g", result: 10}
Ответ от сервера:
{"bet": "g", "result": 10, "reward": {}, "winstreak": 0}
█████████ > Создадим запрос, в котором выбранный цвет будет соответствовать цвету числа:
{bet: "g", result: 0}
Ответ от сервера:
{bet: "g", result: 0, reward: {}, winstreak: 1}
█████████ > Повторим запрос ещё 49 раз и получим ответ:
{bet: "g", result: 0, reward: {Public code: "6bgd5pass6", Secret code: "asq6xcensn"}, winstreak: 50}
█████████ > При создании сайтов нужно внимательно проверять, какие данные можно создавать на стороне клиента, а какие нужно генерировать на сервере.
🌄VIP_Zone > Credentials found: 02_01_2021-22_00.conf
█████████ > На этот раз в рулетке нужно угадать число от 0 до 63. Набрать 50 побед раньше остальных удалось RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (33).
RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (33) > Увидев что используется Python и функция randint(), я понял что числа получаются псевдослучайным алгоритмом, и на сайте указан seed для этой генерации. Используя seed, я прогнал в цикле кол-во попыток + 50, чтобы понять, какие числа будут выпадать следующие 50 раз.
█████████ > Зная алгоритм генератора и имея зерно, можно легко восстановить последовательность:
Код на языке Python3:
ATTEMPTS = 0 # Количество уже совершенных попыток на момент запуска кода
import random
random.seed(1796849247)
for i in range(ATTEMPTS+50):
    n = random.randint(0, 63)
    if i >= ATTEMPTS:
        print(i, n)
Результат, показывающий количество попыток и следующее число:
0 34
1 63
2 40
3 9
4 59
5 16
...
█████████ > Последовательно введем числа, которые рассчитала программа, и получим 50 побед подряд:
█████████, твоя серия побед: 50 (Попыток: 50)
Public code: 7hfp8request5
Secret code: t4nbcb4bub
█████████ > Каким бы хорошим ни был алгоритм для генерации псевдослучайных чисел, это всё ещё алгоритм. Зная все его параметры, его можно воспроизвести.
█████████ > Обычно, зерно алгоритма устанавливается вручную только в том случае, когда требуется возможность воспроизведения алгоритма. Например, в играх с процедурной генерацией, сид позволяет игрокам постоянно получать одинаковый результат.
█████████ > Если возможность воспроизведения не нужна, обычно зерно не устанавливают. В таком случае система сама выбирает сид случайным или псевдослучайным образом.
🌄VIP_Zone > Credentials found: 03_01_2021-20_30.conf
█████████ > Диапазон чисел для угадывания теперь от 0 до 255. Информацию за 50 побед первым получил SD|⬛🇺🇸AsteroidBaker (33).
SD|⬛🇺🇸AsteroidBaker (33) > Увидел, что нужно хешировать не само число, а его байтовое значение. Написал маленькую программу - получил хеш-сумму каждого числа, а дальше ctrl+f...
Если интересна программа:
import hashlib
for i in range(256):
    hash_object = hashlib.md5(i.to_bytes(1, byteorder='big'))
    print(i, hash_object.hexdigest())
█████████ > Действительно, число от 0 до 255 занимает как раз 1 байт. Логично предположить, что хэширование производится именно этого байта, а не строкового представления числа.
█████████ > Хэширование часто применяется, чтобы доказать, что число известно заранее и не будет выбрано в зависимости от ваших действий. При корректной реализации это позволяет избежать подкрутки, не раскрывая само число.
█████████ > Для защиты от брутфорса, обычно показывают только часть хэша. В нашем случае это тоже так. Сбрутить хэш мы не можем, но мы знаем, что значений всего 256, а значит можно создать хэши для каждого из них:
0: 93b885adfe0da089cdf634904fd59f71
1: 55a54008ad1ba589aa210d2629c1df41
2: 9e688c58a5487b8eaf69c9e1005ad0bf
3: 8666683506aacd900bbd5a74ac4edf68
4: ec7f7e7bb43742ce868145f71d37b53c
5: 8bb6c17838643f9691cc6a4de6c51709
...
█████████ > Остаётся только найти показанную часть хэша в списке 50 раз подряд и получить результат:
█████████, твоя серия побед: 50
Hash: 02129bb861061d1a...; Previous hash: f37c6f3896b2c85f...
Public code: 8vzy9config2
Secret code: vcvsb2n7kf
█████████ > Основная причина возможности такой атаки - маленький диапазон входных значений, которые можно заранее просчитать.
█████████ > Для защиты от атак на хэши методом предварительной генерации таблиц значений, используют соль - случайные данные, которые хэшируются вместе с исходными.
█████████ > В зависимости от особенностей задачи, соль может быть открытой или секретной.
█████████ > Например, для доказательства предварительной генерации случайного числа используют секретную соль. Например, к числу можно дописать случайную строку или генерировать число для хэширования в большем диапазоне, а потом приводить к нужному.
█████████ > При хранении паролей, наоборот, нужна возможность постоянного хранения соли, поэтому обычно её хранят вместе с хэшем. В таком случае, новую таблицу нужно будет создавать для каждоко пароля, что значительно усложнит атаку. Кроме того, будет невозможно быстро найти другие аккаунты с тем же паролем.
🌄VIP_Zone > Credentials found: 04_01_2021-19_00.conf
█████████ > На этот раз угадываем число от 0 до 1023. Информацию за 50 побед первым получил RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (33).
RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (33) > Я начал изучать про регистр сдвига, а точнее про периодичность. Так как спустя N итераций все пойдет заново, я просто отправил 1024-1 запроса, собрав ответы в массив, а потом просто повторил первые 50 результатов, так как они пошли по кругу
█████████ > Регистр сдвига, как и некоторые другие генераторы псевдослучайных чисел, высчитывает новое значение от предыдущего.
█████████ > Это означает, что значения, идущие после некоторого числа будут каждый раз одинаковыми.
█████████ > Так как диапазон значений ограничен, рано или поздно мы попадём в одно из ранее полученных значений, после чего процесс зациклится.
█████████ > Мы можем записывать, в каком порядке идут числа, и узнать последовательность для всего витка цикла:
525
262
643
833
416
...
█████████ > После этого сделаем ещё 50 попыток по уже известному циклу и получим результат:
█████████, твоя серия побед: 50
Public code: 6cnt5ssrf4
Secret code: 9hmhwk7pyi
█████████ > Несмотря на возможность такой атаки, регистр сдвига с линейной обратной связью часто используют благодаря его простоте.
█████████ > Для защиты от атаки на цикличность в генераторах применяют три очень простых метода.
█████████ > Например, можно скомбинировать несколько простых генераторов и добавить их влияние друг на друга. Это позволит в разы увеличить длину цикла.
█████████ > Того же эффекта можно добиться, если сгенерировать число в большем диапазоне (а значит с более длинным циклом), а затем привести к нужному.
█████████ > Третий способ позволяет избежать зацикливания, просто не используя последовательные значения. Если между использованиями генератора будет непостоянное количество неиспользованных сгенерированных чисел, то предугадать результат станет невозможно.
🌄VIP_Zone > Credentials found: 05_01_2021-17_30.conf
█████████ > Диапазон угадываемых чисел на этот раз от 0 до 4096. Первым 50 побед и ценную информацию получил RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (33).
RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (33) > Сам метод, что указан в комментарии от 🔸vladko312 подразумевает, что следующие числа считаются от предыдущего. Так нашел недостающие элементы функции. Так как чисел 4096 и поведение младших битов довольно предсказуемо, то я попробовал разделить число на 2 в 52 степени, и получил то же число, что и выпало в рулетке. Дальше я просто повторил это 50 раз и получил итог
█████████ > Линейный конгруэнтный метод генерации псевдослучайных чисел вычисляет новые элементы последовательности от предыдущих по такой формуле:
Xi+1 = aXi + c (mod m)
█████████ > Модуль нам известен из комментария на изображении.
m = 264
█████████ > Заметим, что для разности последовательных элементов можно тоже получить формулу:
Xi+1 - Xi (mod m)
(aXi + c) - (aXi-1 + c) (mod m)
aXi + c - aXi-1 - c (mod m)
aXi - aXi-1 (mod m)
a(Xi - Xi-1) (mod m)
█████████ > Соответственно, число a можно вычислить, зная три последовательных вывода генератора:
a = (Xi+1 - Xi) / (Xi - Xi-1) (mod m)
Xi-1 = 5397338601576430726
Xi = 6858012819036222805
Xi+1 = 1476828513156171712
a = (1476828513156171712 - 6858012819036222805) / (6858012819036222805 - 5397338601576430726) (mod 264)
a = 13065559767829500523 / 1460674217459792079 (mod 264)
a = 7561471816851962789
█████████ > Теперь узнаем c, используя два последовательных числа:
c = Xi+1 - aXi (mod m)
c = 1476828513156171712 - 7561471816851962789 * 6858012819036222805 (mod 264)
c = 1476828513156171712 - 8449699700643854025 (mod 264)
c = 11473872886221869303
█████████ > Теперь, мы знаем все элементы формулы и можем посчитать последующие элементы:
Xi+2 = aXi+1 + c (mod m)
Xi+2 = 7561471816851962789 * 1476828513156171712 + 11473872886221869303 (mod 264)
Xi+2 = 14899029625091112640 + 11473872886221869303 (mod 264)
Xi+2 = 7926158437603430327
█████████ > Числа у нас генерируются по модулю 264, а нам нужны по модулю 212. Разделим на 252 и получим ответ:
Xi+2 = 7926158437603430327 Xi+2 / 252 = 1759
█████████ > Осталось повторить ещё 49 раз, чтобы получить информацию.
█████████, твоя серия побед: 50
Generated number: 1201189855780377626
Public code: 6dtn2http6
Secret code: ig8u8yvec4
█████████ > Хоть на этот раз генератор и был цикличным, генерировать весь цикл было бы слишком долго.
█████████ > Несмотря на это, мы смогли восстановить внутреннее состояние генератора и воспроизвести последовательность.
█████████ > Из-за простой математики, линейный конгруэнтный генератор не получится применить в задачах, требующих криптографической сложности.
█████████ > Для некриптографических задач, например в статистике, генератор активно применяется благодаря простоте вычислений и неплохому распределению значений.
🌄VIP_Zone > Credentials found: 06_01_2021-16_00.conf
█████████ > Числа в этот раз нужно угадывать в том же диапазоне от 0 до 4096. И снова ценную информацию за 50 побед получил RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (33).
RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (33) > Заметив, что снова чисел 4096, и алгоритм тот же, я по числам начал подбирать возможные модули, и подбором понял, что в данном случае не 64 бита, а 256. Дальше я просто заново повторил все то же самое
█████████ > Действительно, алгоритм тот же, но на этот раз неизвестен модуль. Обозначим разность последовательных элементов как Ti:
Ti = Xi+1 - Xi
Ti = a(Xi - Xi-1)
Ti = aTi-1
█████████ > Посчитаем некоторую формулу от Ti и обозначим её как Si:
Si = Ti+1 * Ti-1 - Ti2
█████████ > Заметим, что это число делится на m:
Si ≡ a2Ti-1 * Ti-1 - (aTi-1)2 (mod m)
Si ≡ (aTi-1)2 - (aTi-1)2 (mod m)
Si ≡ 0 (mod m)
█████████ > Так, m будет делителем Si, а значит если рассчитать достаточно много таких числел, то m будет их наибольшим общим делителем:
X1 = 35153465990794006912561987908956951410575168227258806087531701749772567403152
X2 = 58952686259279521155478965371025491354653456126096612254758673481846434228397
X3 = 108488372770576729940857903638615768637561319779017991420342718378138869783854
X4 = 26545124233289414038582888408536973450304309494259665015163146160016250666595
X5 = 70283581666461393282096121128161358571353672839295451375245824890374039151324
T2 = 23799220268485514242916977462068539944078287898837806167226971732073866825245
T3 = 49535686511297208785378938267590277282907863652921379165584044896292435555457
T4 = -81943248537287315902275015230078795187257010284758326405179572218122619117259
T5 = 43738457433171979243513232719624385121049363345035786360082678730357788484729
S3 = -4403969659599666492199261687560434104270479560531654855719354764641788046973922521007738362935969891501034347057945835625829933019596196999943710838882304
S4 = -4548081464946315487182458639662005448352517841779193142106770374072014926992176749268210697445192592026447136651430581319543049088920631071779532914556928
НОД(S3, S4) = 115792089237316195423570985008687907853269984665640564039457584007913129639936
НОД(S3, S4) = 2256
█████████ > Используя всего два Si мы получили красивое число 2256. Вероятно, это и есть модуль. Рассчитаем a и c по формулам, которые мы уже использовали:
a = (Xi+1 - Xi) / (Xi - Xi-1) (mod m)
a = (108488372770576729940857903638615768637561319779017991420342718378138869783854 - 58952686259279521155478965371025491354653456126096612254758673481846434228397) / (58952686259279521155478965371025491354653456126096612254758673481846434228397 - 35153465990794006912561987908956951410575168227258806087531701749772567403152) (mod 2256)
a = 49535686511297208785378938267590277282907863652921379165584044896292435555457 / 23799220268485514242916977462068539944078287898837806167226971732073866825245 (mod 2256)
a = 111009624511207822974368285208283111427555459019381982343647054115276707971765
c = Xi+1 - aXi (mod m)
c = 108488372770576729940857903638615768637561319779017991420342718378138869783854 - 111009624511207822974368285208283111427555459019381982343647054115276707971765 * 58952686259279521155478965371025491354653456126096612254758673481846434228397 (mod 2256)
c = 108488372770576729940857903638615768637561319779017991420342718378138869783854 - 95126104955866177358649864057539876354617485095469387062181686368089290742865 (mod 2256)
c = 13362267814710552582208039581075892282943834683548604358161032010049579040989
█████████ > Снова получив все элементы формулы, мы можем сгенерировать новые числа:
X6 = aX5 + c (mod m)
X6 = 111009624511207822974368285208283111427555459019381982343647054115276707971765 * 70283581666461393282096121128161358571353672839295451375245824890374039151324 + 13362267814710552582208039581075892282943834683548604358161032010049579040989 (mod 2256)
X6 = 106908734439352691038324410784897997684506160403857241585007641080128380427660 + 13362267814710552582208039581075892282943834683548604358161032010049579040989 (mod 2256)
X6 = 4478913016747048196961465357285982114180010421765281903711089082264829828713
█████████ > Переведём число в нужный формат, поделив на 2256-12
X6 = 4478913016747048196961465357285982114180010421765281903711089082264829828713 X6 / 2244 = 158
█████████ > Получим информацию, рассчитав ещё 49 чисел.
█████████, твоя серия побед: 50
Generated number: 14994581707760839146184852390544906365174366698005600171789271059595846802858
Public code: 6hpg2pentest5
Secret code: rvnvk4s997
█████████ > Модуль генератора в данном случае гарантированно восстановить невозможно, однако даже при небольшом количестве Si вероятность его найти очень высока.
█████████ > Кроме того, модуль всегда будет делителем того, что мы нашли, но при этом будет больше, чем сгенерированные числа, что тоже позволяет его определить.
█████████ > Если бы казино не публиковало информацию о сгенерированном числе, так легко восстановить внутреннее состояние генератора у нас бы не вышло.
█████████ > Другим методом защиты вывода генератора могло бы послужить его хэширование, т.к. криптографически стойкие хэши скрывают зависимости между данными.
🌄VIP_Zone > Credentials found: 07_01_2021-14_30.conf
█████████ > Диапазон чисел увеличился и теперь нужно угадать число от 0 до 32767. На этот раз информацию и 50 побед первым получил ⬛🚧❄Куздепись (31).
⬛🚧❄Куздепись (31) > С самого начала было понятно, что нужно поставить сид, равный unix time сервера в ответе + 1, т.к. следующий ответ имел бы эту дату. Для этого в python предназначено много встроенных функций, с помощью которых нехитрыми манипуляциями я смог получить 50 точно угаданных чисел.
█████████ > Для инициализации генератора псевдослучайных чисел действительно используется зерно, равное UNIX timestamp от времени на сервере.
█████████ > Важно отметить несколько моментов, которые стоит учесть при создании своей программы:
 - Время на сервере может быть неточным. Нужно синхронизировать время в программе и на сервере.
 - Ожидание конкретного времени через фиксированные интервалы лучше, чем фиксированная задержка. Используем pause.until() вместо time.sleep()
 - Между отправкой запроса и его получением может быть задержка. Её тоже нужно заранее учесть и генерировать числа для предполагаемого времени получения.
 - Задержка может меняться. Программа будет надёжнее, если сможет сама рассчитывать задержку.
 - В некоторых случаях погрешность задержки может находиться на границе секунд. Желательно делать небольшой сдвиг при изменении задержки.
█████████ > Учитывая все эти особенности, я написал пример программы:
import datetime, requests, random, pause, json
offset = 0
winstreak = 0
cookies = {"invite": "8693b09d85d4c648ec4e6df944f70a20", "token": input("me> ")}
url = "https://netkings.hackerwars.ru/money_time"
stime = datetime.datetime.now()+datetime.timedelta(seconds=1)
while True:
    pause.until(stime)
    tss = stime.strftime("%H:%M:%S %d.%m.%Y")
    random.seed(int(stime.timestamp()) + offset)
    res = requests.post(url, data=json.dumps({"bet": random.randint(0,32767)}), cookies=cookies).json()
    if "reward" in res and res['reward'] != {}:
        print(res['reward'])
        break
    if "winstreak" in res:
        winstreak = res['winstreak']
    if winstreak == 0 and 'time' in res:
        print(tss, offset)
        offset = int((datetime.datetime.strptime(res['time'], "%H:%M:%S %d.%m.%Y") - datetime.datetime.strptime(tss, "%H:%M:%S %d.%m.%Y")).total_seconds())
        stime += datetime.timedelta(milliseconds=50)
    stime += datetime.timedelta(seconds=1)
█████████ > После запуска программа запросит токен /me и начнёт генерировать и отправлять числа.
█████████ > Когда информация за 50 побед будет получена, программа выведет её и завершится:
{'Public code': '4eni6vuln5', 'Secret code': 'etvqirbbpy'}
█████████ > Время часто использовалось для инициализации генераторов в прошлом, так как младшие биты времени ведут себя достаточно случайно.
█████████ > Например, во второй версии языка python использовалось время с точностью до 1/250 секунды, если система не имела своего источника случайности.
█████████ > Сейчас почти все системы обладают источником случайных или псевдослучайных данных, которые и используются для инициализации генераторов.
█████████ > Использование времени с большей точностью усложняет задачу предварительной генерации, но иногда для атаки нужна не предварительная, а повторная генерация.
█████████ > Например, если время используется для инициализации генератора криптографических ключей, можно перебрать время, близкое к предположительному времени генерации, что будет намного быстрее перебора ключа.
█████████ > Многие современные процессоры имеют встроенные генераторы случайных чисел, а ОС могут использовать некоторые биты из вывода драйверов для достижения псевдослучайности.
█████████ > При этом, настоящие ГСЧ, как и качественные ГПСЧ, не всегда могут выдать необходимое количество данных на момент запроса.
█████████ > По этой причине, операционные системы часто реализуют менее качественные, но не блокируемые ГПСЧ.
🌄VIP_Zone > Target: H4rdF0rk_R3c0v3rY...
█████████ > Похоже, 0day 🌄VIP_Zone и на серваке H4rdF0rk работает... Мне понадобится помощь пользователей системы HackerWars, чтобы быстро вытащить всю инфу...
█████████ > Подключиться можно будет командой /connect, которая уже использовалась для применения 0day 🌁SkyBridge
🌄VIP_Zone > Attack completed. Exiting...
█████████ > Похоже, мы и запасной сервак H4rdF0rk смогли захватить. Всё благодаря 0day уязвимости 🌄VIP_Zone, обнаруженной SD|🇺🇸🔸ALoshad (19).
█████████ > Сейчас я изучаю собранные зацепки, а о результатах напишу в этом логе.
█████████ > Скоро тут появится больше информации о применении 0day 🌄VIP_Zone.
user@HackerWars:~/0day $