В один из скучных карантинных вечеров, когда вся работа была переделана, и заняться особо было нечем, я вдруг вспомнил, что давно я не заглядывал в гости к Google)) тем более, у них появилось довольно большое количество облачных сервисов, которые просто грех не прощупать. Работая с Lambda в AWS, мне вдруг захотелось сравнить аналог в Google, который называется Cloud Function в нашем с вами контектсте. Так зачем же отказывать себе в подобных изысканиях, поехали! =)
В процессе использования Cloud Function было обнаружено, что у Хакера имеется возможность загрузить свой зловредный код в тело функции и выполнить reverse shell к серверу Хакера что приводит к выполнению произвольного кода Linux-контейнера. В нашем случае, я буду рассматривать Cloud Function для выполнения Python-скриптов, и для начала, загрузим наш код в функцию через веб-интерфейс Cloud Function:
Pic.1 Reverse-shell code and env command output
В результате вызова функции, происходит reverse-shell от root-пользователя к серверу Хакера, в нашем случае с выводом команды env, которая отображает имеющиеся переменные окружения Linux-контейнера.
Некоторые переменные могут показаться интересными, например:
- X_GOOGLE_SUPERVISOR_HOSTNAME=supervisor
- SUPERVISOR_INTERNAL_PORT=8081
- VIRTUAL_ENV=/env
- PORT=8080
- etc…
Pics.2-3 Linux container and env command output
Пробежавшись по каталогам, можно обнаружить, что в каталогах /env находится большое количество исходных кодов на Python доступных для дальнейшего изучения Хакером. В директории /worker также имеются несколько Python-скриптов, которые запускают саму функцию помещенную в директорию /user_code, которая является симлинком на /tmp.
Pic.4 Worker directory listing
В процессе создания функциии, пользователю предлагается выбрать количество необходимой памяти (RAM) для выполнения функции в контейнере, от выбора зависит конечная стоимость выполнения самой функции. Конечная стоимость выполнения функции рассчитывается ТОЛЬКО из потребления из CPU и выбора RAM.
В процессе выполнения Reverse-shell Хакеру доступны все 2048Mb RAM, даже если пользователь при создании функции выбрал 128Mb. А это означает, что расчёт стоимости считается не правильно, и это можно рассматривать как — УЯЗВИМОСТЬ 1.
Pic.5 Chosing function memory
Pic.6 Memory available
Если вызвать uname -a, то можно обнаружить, что хостовое ядро имеет довольно старую версию, но т.к. контейнер имеет Security Hardening, то проэксплуатировать что-либо из публичных уязвимостей не представляется возможным… но это не точно =))
Pic.7 Host linux kernel
При исследовании Linux-контейнера были обнаружены следующие ограничения:
- Linux-контейнер имеет внешний IP-адрес, но доступ на входящие подключения закрыт — bind-shell недоступен!
- Корневая файловая система доступна только для чтения, кроме /tmp (overlayfs) без возможности remount — установка софта из репозитория недоступна!
Ряд дополнительных наблюдений:
- Было обнаружено, что в Linux-контейнере имеется ряд установленного софта, например wget, который может нам пригодиться в дальнейшем для доставки своего Payload в контейнер.
- Linux-container может совершать исходящие соединения на любой TCP-порт в Internet (1-65535/tcp), устанавливая соединение с любым хостом.
- При инициализации Linux-контейнера, ему назначается новый внешний IP-address.
- Внутри контейнера имеется возможность выставить exec-флаг (chmod +x file) на любой файл загруженные в /tmp директорию — и это может нам пригодиться для загрузки своего Payload — УЯЗВИМОСТЬ 2.
- У Linux-контейнера существует настройка тайм-аута выполнения функции, по умолчанию значение равно 60 сек., максимально допустимое значение — 540 сек. — это означает, что при reverse-shell контейнер закроет TCP-соединение через установленный интервал с сервером и контейнер будет уничтожен.
Pic.8 Function time-out
Визуализируем текущее состояние нашего исследования в виде схемы, от которой в дальнейшем будем выстраивать свою атаку на Linux-контейнер:
Pic.9 Cloud function reverse-shell to Hacker server
Исходя из совокупности вышеописанных факторов, можно провести атаку Reverse-socks, суть которой такова:
- В тело функции помещается зловредный код
- В Linux-контейнер доставляется Payload (port.tar) средствами wget с сервера Хакера — http://167.71.2.108/port.tar , в архиве которого находится бинарник revsocks и shell-script (ссылки ниже)
- Payload архив доставляется в контейнер и распаковывается в /tmp с последующим вызовом init.sh
- Скрипт init.sh вызывает бинарник с определенными аргументами, который в свою очередь устанавливает исходящее соединение от Linux-контейнера к серверу Хакера на 8081/tcp
- На сервере хакера предварительно запущен сервис, который прослушивается на порту 9090/tcp и ожидает входящие подключения от Linux-контейнера на 8081/tcp
Pic.10 Reverse-socks Payload code
В качестве SOCKS-прокси был использован revsocks, который написан на Go, и компилируется в один самодостаточный бинарник, который выступает в роли сервера и клиента — https://github.com/kost/revsocks .
Pic.11 init.sh script
На стороне Хакера запускается серверная часть revsocks:
./revsocks -listen :8081 -socks 167.71.2.108:9090 -pass hammer50 &> ./logs/server.log &
При вызове функции — загружается Payload, контейнер устанавливается TCP-соединение на порт 8081/tcp, Хакер устанавливает соединение со своим сервером на порт 9090/tcp любым SOCKS5-клиентом, например браузером, и выполняет форвардинг трафика от контейнера в Интернет, используя его как прокси сервер:
Pic.12 Browser proxy settings
Pic.13 Traffic forwarding through Linux-container
Далее, трафик успешно форвардится, но TTL Linux-контейнера = 9 мин., после чего контейнер уничтожается, а перезапускать каждый раз контейнер руками — не по Хакерски! =)
Для решения этой задачи быстренько разработаем PoC в виде overlay shell-скрипта (назовем его proxima.sh), который в автоматическом режиме будет менеджить наши Linux-контейнеры, автоматически их создавать, проверять, перезапускать, проверять подключения клиента и SOCKS-сервера:

Pic.14 PoC overlay script for full automation
Хотелось бы сделать пару комментариев к скрипту, если читатель вдруг решит развернуть Хак-прокси у себя. Для этого вам понадобится:
- любой микро-инстанс (512MB RAM, 1 CPU core), например на DO =)
- установить curl и gcloud себе на сервис
- инициировать gcloud
- создать функцию через веб-интерфейс или через тот же gcloud, предварительно в настройках дав возможность вызова функции неавторизованным пользователем (unauth. invoke)
- В переменной FUNC_NAME указать название своей ранее созданной функции (31 строка)
- Открыть микро-инстансе TCP-порты для входящих подключений контейнера и SOCKS-клиент (8081/tcp & 9090/tcp)
- В конце-концов доработать PoC под себя, или вообще разработать свой более качественный xD
Результат выполнения PoC-скрипта выглядит следующим образом:
Pics.15 Automatic management Linux-container as Hacker Proxy Server (HPS)
Как видно на скриншоте выше (Pics.15), через 9 минут происходит автоматическая смена IP-адреса (container reinit) — идеальный вариант для Хакера, который хочет запустить свои malicious-скрипты. В результате, Хакер может атаковать как сервер SUPERVISOR со своего хоста, так и любой хост в Интернет. Схема атаки выглядит следующим образом:
Pic.17 Big picture
Также, в результате исследования было выявлено, что сетевой трафик в рамках Cloud Function не учитывается в стоимости, а это означает, что Хакер может использовать гигабайты трафика, при этом платя только за процессорное время и оперативную память — УЯЗВИМОСТЬ 3.
Pic.18 Download gigabytes through Linux-container
Pic.19 Nmap through SOCKS-proxy server
Для удобства использования нашей рабочей схемы, можно использовать BadVPN, который на уровне ОС имеет возможность установить соединение с SOCKS-сервером и прописать системные роутинги (route / ip r)
Pic.20 BadVPN init script
В результате выполнения скрипта, создается виртуальный сетевой интерфейс hpstun0 взаимодействующий с SOCKS-сервером, добавляется подсеть 169.254.0.0/16, которая маршрутизируется через hpstun0.
Pic.21 Using BadVPN through Hacker server with reverse-socks
После выкачивания гигабайтов трафика , в биллинге мы можем увидеть что расходы на выполнения функции составили ~15руб. (~0.2$), то есть расчёт скачанного трафика через Cloud Function — не учитывается!
Pic.22 Billing information
Vulnerabilities summary
- Ресурсы использования оперативной памяти могут считаться неправильно.
- В Linux-контейнере возможна установка exec-флагов (chmod +) на свой Payload.
- Не учитывается передаваемый сетевой трафик от Cloud Function в Интернет.
- Снижение уровня доверия к диапазонам адресам айпи адресов Google.
Всё вышеизложенное добро было направлено в Google в рамках VRP, на что был получен следующий ответ от Security-команды (реакция < 12 часов в выходной день, много времени видать на Reproduction и анализ потратили xDD):

Но ребятки из Google Security Team не считают вышеизложенное уязвимостями, и не видят какого-либо импакта во всём этом)) Бегать по продуктовым форумам Google и доказывать им что-то там про уязвимости за то, что бы тебе подкинули косточку в виде $$$ — топовая идея конечно, спасибо но нет, иногда дело совсем не в деньгах, а в профессионализме, которым частенько принебригают специалисты в подобных компаниях по ряду объективных и не очень причин =))
Подозреваю, что ребята из Security-команды из-за большого количества своих продуктов не совсем понимают детали работы своих же продуктов xDDD, а в частности расчёты ресурсов между VM instance vs. Cloud Function, об этом они кстати честно признаются в этой презентации

Исходя из этого, я просто не могу не поделиться всем этим добром с хак-сообществом — Юзайте, Товарищи, юзайте!
Ну и по классике жанра, резюмирующая картинка всего этого поста:
