.. | ||
JoJo_exploit.py | ||
readme.md |
разбор таска Отсылка на ДжоДжо 1
Для начала, посмотрим что происходит на странице. Для этого, зайдем в исходный код. На странице выполняется скрипт, который сначала открывает WebSocket
. Чтобы узнать что это, загуглим. Это соединение с сервером, которое обменивается текстовыми данными. Все что есть в скрипте помимо этого является оберткой над этим соединением, по типу добавления сообщения в страницу. Так что дальше исследовать страницу мало пользы, можно исследовать соединение.
Погуглив, можно узнать что общение по вебсокетам доступно в python3, что мы и будем использовать. Программа дальше отрывает соединение, отправляет init
и печатает все ответы от сервера.
import websocket as ws
c=ws.create_connection('wss://jojo1.ctfmay.sch9.ru/ws/')
c.send('b')
while(1):
print(c.recv())
Вывод:
photo 3
Следующий запрос соединение воспримет как попытку угадать фразу. Так как у ДжоДжо изначально 4 ХП, то можно узнать 4 идущие подряд фразы. Дальше, можно воспользоваться данным в условии словарем глаголов, для всех глаголов заменить их номерами и посмотреть на результат. Пример:
Фразы:\
Я хочу выйти чтобы рвать, а потом родиться и, будучи победителем, приказать оставить тебе, ДжоДжо, так сильно пытающийся велеть!!! Я хочу определять чтобы попадать, а потом грозить и, будучи победителем, приказать решиться тебе, ДжоДжо, так сильно пытающийся мыть!!! Я хочу утверждать чтобы хранить, а потом согласиться и, будучи победителем, приказать хотеться тебе, ДжоДжо, так сильно пытающийся смеяться!!! Я хочу обращаться чтобы совершить, а потом возражать и, будучи победителем, приказать меняться тебе, ДжоДжо, так сильно пытающийся сравнивать!!! Я хочу понравиться чтобы следовать, а потом нравиться и, будучи победителем, приказать сметь тебе, ДжоДжо, так сильно пытающийся миновать!!! Номера в 0-нумерации: 41, 924, 260, 514, 210, 565 100, 204, 727, 514, 637, 905 159, 483, 195, 514, 65, 246 218, 762, 662, 514, 492, 586 277, 42, 130, 514, 919, 926
Можно заметить, что номер первого глагола изменяется на одинаковое число каждый раз, возвращаясь в 0 если увеличивается до 999, т.е. "ходит по кругу из чисел от 1 до 999" (), то же верно и для остальных номеров. Можно ещё несколько раз позапускать соединение чтобы убедиться что это всегда так и что номер (?????) глагола - всегда 514. Теперь, с помощью этого замечания попробуем угадать одну фразу. Кроме того, попробуем автоматически генерировать ответ, т.к. в дальнейшем это придется сделать 1000 раз.
import websocket as ws #подключаем библиотеку
a=open('glags.txt','r').read().split('\n')[:-1] #Загружаем файл, записываем в массив
c=ws.create_connection('wss://jojo1.ctfmay.sch9.ru/ws/') #Начинаем соединение
c.send('init') #Посылаем 'init' в соединение
print('recieve:',c.recv()) #Выводим на экран ответ (photo 3)
c.send('b') #Посылаем что-то чтобы узнать первую фразу
s=c.recv() #Считываем фразу из соединения
print('recieve:',s) #Выводим её на экран
print('recieve:',c.recv()) #Считываем оставшиеся фразы
print('recieve:',c.recv()) #
print('recieve:',c.recv()) #
print('recieve:',c.recv()) #
words=[s[s.find('хочу')+5:s.find('чтобы')-1], #Вытаскиваем из фразы только 5 глаголов, которые изменяются...
s[s.find('чтобы')+6:s.find('а потом')-2], # ...и записываем их в массив
s[s.find('а потом')+8:s.find('и, будучи')-1], #
s[s.find('и, будучи')+33:s.find('тебе,')-1], #
s[s.find('пытающийся')+11:s.find('!!!')]] #
print('words in phrase:',words) #Выводим на экран массив слов
oidx=[a.index(words[i]) for i in range(5)] #Записываем индексы слов в массиве a (словаре) в массив
print('indexes in dictionary:',oidx) #И выводим этот массив
c.send('b') #Повторяем операцию для следующей фразы
s=c.recv()
print('recieve:',s)
print('recieve:',c.recv())
print('recieve:',c.recv())
print('recieve:',c.recv())
print('recieve:',c.recv())
words=[s[s.find('хочу')+5:s.find('чтобы')-1],
s[s.find('чтобы')+6:s.find('а потом')-2],
s[s.find('а потом')+8:s.find('и, будучи')-1],
s[s.find('и, будучи')+33:s.find('тебе,')-1],
s[s.find('пытающийся')+11:s.find('!!!')]]
print('words in phrase:',words)
idx=[a.index(words[i]) for i in range(5)]
print('indexes in dictionary:',idx)
d=[(idx[i]-oidx[i])%len(a) for i in range(5)] #Cчитаем изменение номера для каждого глагола
print('differences:',d) #Выводим его на экран
for i in range(5): #Угадываем глаголы третьей фразы
idx[i]+=d[i] #Пересчитываем массив idx
idx[i]%=len(a)
words[i]=a[idx[i]] #Выбираем нужные глаголы
c.send(f'Я хочу { words[0] } чтобы \ #Выводим угаданную фразу
{ words[1] }, а потом { words[2] } и, будучи \ #
победителем, приказать { words[3] } тебе, ДжоДжо\ #
, так сильно пытающийся { words[4] }!!!') #
while(1):
print(c.recv()) #Выводим остаток ответов от сервера.
Видим, что на этот раз вывод другой и ХП снялись уже не у ДжоДжо, поэтому продолжаем отгадывать фразы таким же образом. Начальный код остается таким же, но после выбора нужных глаголов код будет:
for i in range(1000): #Повторяем столько раз, сколько ХП у противника
c.send(f'Я хочу { words[0] } чтобы \ #Отправляем сгенерированную фразу
{ words[1] }, а потом { words[2] } и, будучи \
победителем, приказать { words[3] } тебе, ДжоДжо\
, так сильно пытающийся { words[4] }!!!')
s=c.recv() #Выводим ответ от сервера
print('recieve:',s)
print('recieve:',c.recv())
print('recieve:',c.recv())
print('recieve:',c.recv())
print('words in phrase:',words)
print('idxs:',idx)
for i in range(5): #Пересчитываем массивы
idx[i]+=d[i]
idx[i]%=len(a)
words[i]=a[idx[i]]
c.send('h') #Выводим что-то чтобы получить ответ от сервера
while(1):
print(c.recv()) #Выводим остаток ответов от сервера.
Таким образом, получаем флаг.
Флаг: ctf{guess_reverse_jusddf}