Уязвимость в тени: Найди скрытый баг в коде | Часть 1

Каждый день мы сталкиваемся с багами, которые прячутся у всех на виду: будь то проблемы с управлением памятью или тонкие логические ошибки – все они кроются в исходном коде. В нашей новой серии, «Уязвимость в тени», мы приглашаем вас поиграть в детектива. Ваша задача проста: изучите фрагмент кода, определите уязвимость и устраните ее. Справитесь с задачей и получите главное вознаграждение – укрепленные навыки безопасного программирования и острое чувство победы над багами!
Задачи
Начнем тогда с C — язык, который одновременно и обожают, и печально известном своими уязвимостями, мы часто видим небрежное использование функций, которое может привести к серьезным недостаткам в системе безопасности. Для решения нашей первой задачи мы начнем с классического примера.
1 C — Магические функции
Найдите уязвимость в коде
1 C – [Спойлер] Ответ
Проблема
Думаю вы хорошо справились с первой задачей. Уязвимость в строке 5, функция scanf -Спецификатор формата %s в функции scanf используется для считывания строки в буфер. Однако, если длина входной строки превышает размер буфера, происходит переполнение буфера. По умолчанию scanf продолжает считывать символы до первого пробельного символа, поэтому при отсутствии пробелов длинная строка может перезаписать память за пределами выделенного буфера.
Решение
Чтобы избежать переполнения, нужно ограничить количество считываемых символов. Для буфера размером 10 символов следует считывать не более 9 символов (один символ оставляем для \0). Это делается с помощью спецификатора формата «%9s».
scanf("%9s", buffer);
считывает максимум 9 символов, предотвращая переполнение.
Последствия: DOS, Уязвимости памяти
2 C — Магические функции 2
Найдите уязвимость в коде
2 C – [Спойлер] Ответ
Проблема
Уязвимость в строке 19, 20 – strcpy() и 17 — gets() Небезопасные функции в C и C++ часто связаны с обработкой строк и памяти без проверки размера данных, что приводит к уязвимости переполнения буфера как strcpy(). Если злоумышленник передаст данные, превышающие размер буфера, программа может переписать соседние области памяти.
Решение
Используйте безопасные альтернативы с проверкой границ:
- Замените gets на gets_s, которая проверяет размер ввода.
- Замените strcpy на strcpy_s, чтобы гарантировать соответствие данных размеру буфера.
Функции gets_s и strcpy_s предотвращают переполнение буфера за счет проверки размеров. Для их использования нужно определить макрос __STDC_WANT_LIB_EXT1__ перед подключением стандартных библиотек. Также можно использовать платформенные аналоги, такие как StringCbCopyA для Windows или strlcpy для FreeBSD.
Последствия: Отказ в обслуживании (DoS), Выполнение кода
Java — Хотя Java известна своей безопасностью благодаря строгой системе типов и управлению памятью, неправильная конфигурация, уязвимости в сторонних библиотеках и ошибки в логике кода могут привести к проблемам безопасности.
Java: язык, который обещает безопасность, но…
Java всегда хвастался своей безопасностью. «Пиши один раз, запускай везде», говорили они. Правда, забыли добавить: «и уязвимости тоже будут запускаться везде».
Этот язык — как тот друг, который всегда носит шлем на велосипеде, но умудряется споткнуться на ровном месте. Казалось бы, строгая типизация, управление памятью через сборщика мусора, JVM, которая должна держать всё под контролем… Но стоит тебе расслабиться, и вот уже Log4Shell приветливо машет из угла, напоминая, что даже простая строка логирования может превратиться в портал для хакеров.
Java-приложения любят работать медленно, но зато уязвимости в них появляются быстро. И неважно, что ты написал свой код идеально — где-нибудь в глубинах зависимостей прячется библиотека, написанная в 2010 году, с уязвимостью, которая активируется ровно в полнолуние.
А самое смешное? Даже если ты нашёл и исправил уязвимость, через неделю выйдет обновление, которое добавит новую. Потому что Java — это не просто язык программирования. Это стиль жизни с лёгкой ноткой паранойи.
3 – Java Когда сервер шпионит за самим собой
Найдите уязвимость в коде
3 Java – [Спойлер] Ответ
Проблема
В этом уязвимом фрагменте кода проблема заключается в том, что параметр id, в строке 16, введенный пользователем, напрямую добавляется к URL-адресу без какой-либо проверки или кодирования. Это означает, что все, что пользователь вводит в поле id, добавляется непосредственно к URL-адресу, формируя окончательный запрос к http://example.com/api/user/{id}.
Понимание и устранение SSRF-уязвимостей обхода пути в Java-приложениях
При разработке веб-приложений очень важно понимать, что неправильная обработка пользовательского ввода может подвергнуть ваше приложение серьезным уязвимостям. Одной из таких уязвимостей является подделка запросов со стороны сервера (Server-Side Request Forgery, SSRF) и ее особый подкласс, Path Traversal SSRF.
Path Traversal — это уязвимость, при которой злоумышленник может получить доступ к файлам и директориям, находящимся за пределами корневой директории приложения. Используя специальные символы вроде ../, атакующий может читать конфиденциальные файлы, такие как /etc/passwd , или даже изменять данные, если есть соответствующие права.
SSRF (Server-Side Request Forgery) — это уязвимость, при которой злоумышленник заставляет сервер отправлять запросы к другим ресурсам, зачастую внутренним. Через SSRF можно получить доступ к закрытым API, метаданным облачных сервисов или другим защищённым ресурсам, к которым у самого атакующего доступа нет.
Обе уязвимости могут привести к утечке данных, компрометации сервера и другим серьёзным последствиям. Давайте разберем пример безопасного кода, чтобы понять, где кроется проблема, почему она опасна и как ее исправить.
Решение
В безопасной версии кода, реализован два ключевых изменения:
URL-кодирование пользовательского ввода строка 18, 19: Параметр id кодируется в URL с помощью URLEncoder.encode(id, StandardCharsets.UTF_8). Этот процесс преобразует потенциально вредоносные символы, такие как ../, в безопасный кодированный формат (%2E%2E%2F), что не позволяет интерпретировать их как команды обхода каталога.
Перемещение пользовательского ввода в строку запроса: Вместо того чтобы добавлять пользовательский ввод непосредственно в путь URL, он включается в качестве параметра запроса (?id=encodedId). Такой подход снижает риск обхода пути, поскольку параметры запроса с меньшей вероятностью могут быть неверно истолкованы сервером как часть структуры каталогов.
Пример безопасного кода:
String encodedId = URLEncoder.encode(id, StandardCharsets.UTF_8); URL url = new URL("http://example.com/api/user/?id=" + encodedId);
Последствия: Несанкционированный доступ, Выполнение кода, Утечки данных
4 – Java Когда код решает пожить своей жизнью
Найдите уязвимость в коде
4 Java – [Спойлер] Ответ
Проблема
В представленном уязвимом коде проблема связана с тем, как обрабатывается пользовательский ввод при выполнении системных команд. Приложение берет параметр команды из URL-запроса и напрямую использует его для создания нового объекта CommandLine строка 30, который затем выполняется DefaultExecutor строка 29. Такой подход опасен тем, что открывает приложение для атак внедрения команд ОС. Манипулируя параметром команды, злоумышленник может внедрить вредоносные команды, что может привести к несанкционированному доступу, краже данных или даже полной компрометации системы.
Например, если злоумышленник передаст параметр типа /bin/ls; rm -rf /, приложение выполнит как листинг каталога, так и удаление всех файлов на сервере. Последствия таких уязвимостей весьма серьезны, включая утечку данных, нарушение работы сервисов и даже полный контроль над сервером, если приложение запущено с высокими привилегиями.
Решение
Чтобы решить эту проблему, безопасный код вводит механизм проверки, определяя список разрешенных команд. Выполняются только команды, явно указанные в списке, такие как /bin/ls и /bin/cat. Это гарантирует, что произвольные команды не могут быть внедрены и запущены. Благодаря использованию абсолютных путей и предопределенного списка безопасных команд приложение снижает риск инъекции команд ОС.
В сценариях, где необходимо динамическое выполнение команд, очень важно тщательно обеззараживать пользовательский ввод. Использование таких библиотек, как org.apache.commons.exec.CommandLine.addArguments(), помогает нейтрализовать опасные символы и предотвратить инъекционные атаки. Однако лучшей практикой остается избегать выполнения системных команд с пользовательским вводом, по возможности используя собственные методы, предоставляемые языком программирования.
Таким образом, уязвимый код подвергает приложение серьезным рискам безопасности из-за некорректной обработки пользовательского ввода при выполнении системных команд. Проверяя команды по белому списку и санируя вводимые данные, приложение может защитить себя от уязвимостей, связанных с инъекциями команд ОС, обеспечивая более безопасную и надежную систему.
Последствия: Несанкционированный доступ, Выполнение кода, Утечки данных
5 – PHP угадай, что исполнить
Найдите уязвимость в коде
5 PHP– [Спойлер] Ответ
Проблема
Динамическое выполнение кода возникает, когда приложение обрабатывает и исполняет код, сформированный на основе пользовательских данных. Использование функций вроде eval в PHP позволяет запускать произвольный код, что открывает возможности для атак злоумышленников. Ввод пользователя передается напрямую в функции динамического выполнения кода, такие как eval, exec, system, что позволяет атакующему ввести собственный код. Если приложение не фильтрует входные данные должным образом, злоумышленник может выполнить команды для доступа к файлам, изменения данных или остановки сервисов. Уязвимости усиливаются при наличии ошибок в конфигурации инфраструктуры, таких как неправильно настроенные кластеры Docker, открытые порты, слабые брандмауэры или WAF и некорректный контроль доступа.
Решение
Необходимо избегать использования функций динамического выполнения кода, таких как eval, exec или system. Следует использовать статические методы и стандартные функции языка для выполнения операций. Если необходимо обрабатывать пользовательский ввод, нужно применять безопасные методы передачи данных через параметры, а не как часть кода. Важно ограничить возможные команды с помощью списка разрешенных значений (whitelist) как показано в безопасном коде, принимая только строго определенные команды или их идентификаторы. Регулярная проверка конфигурации инфраструктуры на наличие уязвимостей и слабых мест в защите также является важной мерой.
Последствия: Несанкционированный доступ, Выполнение кода
Заключение
Наша серия о «Уязвимость в тени», как легко уязвимости могут скрываться в самых неожиданных местах кода. От управления памятью до выполнения команд — каждая ошибка может стать дверью для злоумышленников. Если вы справились со всеми задачами и нашли все уязвимости, поздравляем: вы не просто разработчик, вы настоящий специалист по кибербезопасности. Ваше внимание к деталям и способность мыслить как атакующий делают вас незаменимым в защите приложений от угроз. Продолжайте совершенствоваться, ведь в мире кибербезопасности всегда найдется новая уязвимость, которую нужно выявить и устранить!
Ссылки на материалы о «безопасном коде»
[1] OWASP — https://owasp.org/Top10/
[2] CWE — https://cwe.mitre.org/index.html
[3] Stig Viewer — https://stigviewer.com/