Konteneryzacja skryptów za pomocą Dockera dla pentesterów

Niezależnie od tego gdzie pracujemy w branży IT, styczność z Dockerem jest nieunikniona. Wykorzystywany w projektowaniu aplikacji, dostarcza nam możliwości ich łatwej przenaszalności, skalowalności oraz niezależności. Od kilku lat oprogramowanie to zyskuje na popularności ze względu na swoje możliwości. Ale zacznijmy od początku.

Konteneryzacja a wirutalizacja

Aby zrozumieć jak działa Docker, najpierw musimy zrozumieć powód powstania konteneryzacji oraz jego różnice w porównaniu z tradycyjną wirtualizacją.

|                             |      |                             |
|       VIRTUAL MACHINE       |      |          CONTAINERS         |
|                             |      |                             |
|                             |      |                             |
|      App1    |    App2      |      |      App1    |    App2      |
|  Bins / libs | Bins / libs  |      |  Bins / libs | Bins / libs  |
|    Guest OS  |  Guest OS    |      |       Container daemon      |
|          Hypervisor         |      |                             |
|    Host operating system    |      |    Host operating system    |
|       Infrastructure        |      |       Infrastructure        |
|                             |      |                             |

Wirtualizacja

Standardowa wirutalizacja zakłada utworzenie stosu składającego się z (od najniższego poziomu) warstwy sprzętowej, zainstalowanego systemu operacyjnego, jego nadzorcy (hypervisor) oraz (w zależności od potrzeb) maszyn wirtualnych. Wykorzystują one niezależne od siebie biblioteki oraz pliki binarne aby docelowo utworzyć aplikację.

Proces nie jest skomplikowany, jednak implikuje on sporo wad. Najważniejszą z nich jest spadek wydajności aplikacji spowodowany utworzeniem (w porównaniu do konteneryzacji) dodatkowych warstw tj. systemu operacyjnego działającego na maszynie wirtualnej oraz hypervisora. Każdy z systemów operacyjnych wykorzystuje zasoby w postaci procesora, pamięci RAM czy dysku twardego, co spowalnia cały system.

Konteneryzacja

Potrzebę min. wyższej wydajności konteneryzacja rozwiązuje poprzez zrezygnowanie z zduplikowanej warstwy sytemu operacyjnego oraz hypervisora. Mimo to zachowywane są wszystkie najważniejsze zalety wirtualizacji takie jak przenaszalność czy też seperacja środowiska (choć istnieją podatności, które pozwalają na „wyjście” z niego 😉 Zajmiemy się tym w kolejnych postach).

Należy jednak pamiętać o tym, że nie w każdym przypadku konteneryzacja jest w stanie zastąpić tradycyjną wirtualizację. Bezpieczeństwo wykorzystywania konteneryzacji (bo te aspekty interesują nas najbardziej :)) jest na niższym poziomie niż w przypadku wirtualizacji. Mimo, że Docker rozbija aplikację na mniejsze części (kontenery), to współdzielą one dostęp do jednego systemu operacyjnego hosta. Wprowadza to na przykład ryzyko uruchomienia kontenerów dockera z niekompletną izolacją.

Docker

Powyżej wspomnieliśmy o tym, że „Docker rozbija aplikację na kontenery”. Czym jest więc Docker? Wikipedia definiuje go jako:

Docker – otwarte oprogramowanie służące jako „platforma dla programistów i administratorów do tworzenia, wdrażania i uruchamiania aplikacji rozproszonych”. Docker jest określany jako narzędzie, które pozwala umieścić program oraz jego zależności w lekkim, przenośnym, wirtualnym kontenerze, który można uruchomić na prawie każdym serwerze z systemem Linux

Wikipedia

Uogólniając powyższą definicję, narzędzie Docker pozwala nam na projektowanie aplikacji mających wiele niezależnych od siebie modułów (kontenerów), mających swoje własne, „zamknięte” środowisko. W teorii kontenery nie wiedzą o swoim istnieniu. W praktyce jednak mogą się one komunikować ze sobą na ściśle określonych zasadach.

Po co mi Docker skoro nie jestem administratorem lub programistą?

Tutaj dochodzimy do sedna. Konteneryzacja wydzielonych procesów aplikacji nie przydaje się jedynie w aplikacjach webowych. Jako pentester można użyć Dockera do utworzenia łatwo przenaszalnej aplikacji mającej zautomatyzować uruchamianie setki skryptów wyszukujących podatności. W ten sposób omija się problem z instalowaniem na nowo języków i bibliotek na innym systemie. Z kolei tester ds. automatyzacji wykorzysta Dockera w codziennej pracy z porównywaniem obrazów oraz zrzutów ekranu do testowania UI. Wyklucza to problem wyświetlania się responsywnych elementów na stronie w inny sposób na każdym z komputerów.

Dockerfile

Podstawowym elementem na którym operuje Docker jest Dockerfile. Przechowywanie są w nim wszystkie informacje o pojedynczym obrazie z którego tworzymy kontener.

Przyjżymy się przykładowi. Zakładamy, że zależy nam na utworzeniu środowiska do automatyzacji kilku narzędzi, w tym xira. Zawartość katalogu przetrzymującego nasze skrypty:

┌──(bugspace㉿kali)-[~/Desktop/myScripts]
└─$ ls                                                                             
Dockerfile  xira

Przykładowa zawartość pliku Dockerfile mogłaby wyglądać w następujący sposób:

FROM ubuntu:18.04
COPY . /scripts
RUN set -xe \
    && apt-get update -y \
    && apt-get install -y python3-pip
RUN pip3 install --upgrade pip
RUN pip3 install -r /scripts/xira/requirements.txt
CMD python3 /scripts/xira/xira.py -u https://bugspace.pl

Przyjrzyjmy się kolejnym częściom pliku aby dowiedzieć się, co się dzieje.

  • FROM – tworzy warstwę ze wskazanego obrazu.
  • COPY – jak sama nazwa wskazuje, kopiuje wyznaczone pliki (w naszym przypadku kropka oznacza całą zawartość katalogu) do wskazanego miejsca wewnątrz obrazu (w naszym przypadku jest to folder scripts).
  • RUN – wykonuje wskazane polecenia. Stan kontenera jest zapisywany w obrazie dopiero po wykonaniu wszystkich komend RUN.
  • CMD – jest to komenda, która zostanie uruchomiona po uruchomieniu obrazu.

W powyższym przykładzie najpierw tworzymy warstwę z obrazu ubuntu:18.04, następnie kopiujemy wszystkie pliki z obecnego katalogu do folderu „scripts”, instalujemy i aktualizujemy pip oraz pobieramy potrzebne biblioteki aby uruchomić skrypt xira.

Budowanie obrazu

Aby z naszego pliku Dockerfile utworzyć obraz, należy użyć komendy:

docker build -t scriptsimage -f Dockerfile .

Parametrem „-t” (od słowa tag) definiujemy nazwę oraz tag naszego obrazu w formacie „name:tag”. Z kolei „-f” służy do wskazania pliku obrazu. Po uruchomieniu powyższego polecenia, w odpowiedzi powinniśmy dostać informacje zbliżone do poniższych.

eSnding build context to Docker daemon  1.305MB


Step 1/6 : FROM ubuntu:18.04
18.Sending build context to Docker daemon  1.305MB


Step 1/6 : FROM ubuntu:18.04
18.04: Pulling from library/ubuntu
feac53061382: Pulling fs layer
feac53061382: Verifying Checksum
feac53061382: Download complete
feac53061382: Pull complete
Digest: sha256:7bd7a9cdc9f8ewbf69c4b6212f6432af8e243f97ba13abgr3e641e03a7ewb59e8
Status: Downloaded newer image for ubuntu:18.04
 ---> 39a8cfeef173
Step 2/6 : COPY . /scripts
 ---> bbcad8f5d005
Step 3/6 : RUN set -xe     && apt-get update -y     && apt-get install -y python3-pip
 ---> Running in ca5f1154a80e
+ apt-get update -y
Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
(...)
Updating certificates in /etc/ssl/certs...
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
Removing intermediate container ca5f1154a80e
 ---> 6b5a7d6e8192
Step 4/6 : RUN pip3 install --upgrade pip
 ---> Running in b3a18b37c4f8
Collecting pip
  Downloading https://files.pythonhosted.org/packages/8a/d7/f505e91e2cdea54cfcf51f43c478a8cd64fb3bc1042629ceddk50d9a6a9b/pip-21.2.2-py3-none-any.whl (1.6MB)
Installing collected packages: pip
  Found existing installation: pip 9.0.1
    Not uninstalling pip at /usr/lib/python3/dist-packages, outside environment /usr
Successfully installed pip-21.2.2
Removing intermediate container b3a18b37c4f8
 ---> 13454eef844d
Step 5/6 : RUN pip3 install -r /scripts/xira/requirements.txt
 ---> Running in 8db10b181f06
Collecting beautifulsoup4==4.9.3
(...)
Removing intermediate container 8db10b181f06
 ---> cc35d7c4ef10
Step Removing intermediate container 8db10b181f06
 ---> cc35d7c4ef10
Step 6/6 : CMD python3 /scripts/xira/xira.py -u https://bugspace.pl
 ---> Running in dbfd45c84d87
Removing intermediate container dbfd45c84d87
 ---> c997586dc481
Successfully built c997586dc481
Successfully tagged scriptsimage:latest

Możemy zauważyć, że nasz obraz dockera został utworzony. Listę dostępnych obrazów możemy wyświetlić poleceniem docker images. W następstwie dostaniemy:

┌──(bugspace㉿kali)-[~/Desktop/myScripts]
└─$ docker images  
REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
scriptsimage   latest    1aed353475ad   2 minutes ago   503MB
ubuntu         18.04     39a8cfeef173   8 days ago      63.1MB

Jak widać w outpucie powyżej, ponieważ dla obrazu „scriptsimage” nie wskazaliśmy żadnego tagu, zostanie on uzupełniony o nazwę „latest”.

Uruchamianie kontenera na podstawie istniejącego obrazie

Teraz, mając utworzony obraz, możemy go uruchomić i tym samym stworzyć na jego podstawie kontener. Posłuży nam do tego polecenie docker run -i <IMAGE_ID>. Należy pamiętać, że w tym przypadku uruchamiamy kontener w oparciu o id istniejącego obrazu.

──(bugspace㉿kali)-[~/Desktop/myScripts]
└─$ docker run -i 1aed353475ad
                                  _  __ ________  ___ 
                                 | |/ //  _/ __ \/   |
                                 |   / / // /_/ / /| |
                                /   |_/ // _, _/ ___ |
                               /_/|_/___/_/ |_/_/  |_|
                                                                             

          ~#  Coded by Adhrit.  twitter -- @xadhrit 
          ~#  Contributor: Naivenom.  twitter -- @naivenom
(...)

Po uruchomieniu polecenia, wskazany wcześniej skrypt powinien się uruchomić wewnątrz kontenera. Gdy komenda się skończy, możemy poleceniem „docker ps -a” sprawdzić istniejące kontenery.

┌──(bugspace㉿kali)-[~/Desktop/myScripts]
└─$ docker ps -a  
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS                          PORTS     NAMES
eaf3ff93f771   1aed353475ad   "/bin/sh -c 'python3…"   2 minutes ago   Exited (1) About a minute ago             brave_hypatia

Aby uniknąć tworzenia nowego kontenera za każdym razem, gdy chcemy uruchomić w nim wskazane wcześniej polcenie CMD, możemy uruchomić ponownie kontener poleceniem docker start -i <CONTAINER_ID>. Ok, ale co gdy będziemy chcieli „wejść” do kontenera, aby wywołać jakieś customową komendę? Z pomocą przychodzi nam polecenie docker run -it <IMAGE> bash, dzięki któremu będziemy mogli wejść do powłoki bash. Będąc w niej, możemy używać już standardowych poleceń, przykładowo:

──(bugspace㉿kali)-[~/Desktop/myScripts]
root@decc8a95f3aa:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  scripts  srv  sys  tmp  usr  var
root@decc8a95f3aa:/# pwd 
/
root@decc8a95f3aa:/# 

Platformy o których warto wiedzieć

docker-hub

Jest to proste w obsłudze repozytorium obrazów dockera. Możemy na nim tworzyć własne obrazy, zarządzać nimi i udostępniać community.

Labs play with docker

Platforma do nauki oraz zabawy z dockerem. Na platformie mamy możliwość uruchamiania procesów dockera, budowania kontenerów i generalnego testowania ich.

Cheatsheet podsumowujący

Lista przydatnych poleceń, które warto mieć pod ręką przy pracy z dockerem.

  • docker build -t <NAME>:<TAG> -f <FILENAME> . – utworzenie obrazu dockera o nazwie <NAME>, tagu <TAG> z pliku <FILENAME>
  • docker images – wyświetlenie obrazów dockera
  • docker ps -a – wyświetlanie kontenerów dockera
  • docker rm <CONTAINER_ID> – usunięcie kontenera dockera
  • docker rmi <IMAGE_ID> – usunięcie obrazu dockera
  • docker run -i <IMAGE_ID> – uruchomienie kontenera
  • docker start -i <CONTAINER_ID> – uruchomienie kontenera
  • docker run -it <IMAGE> bash – uruchomienie powłoki bash dla wskazanego obrazu
  • docker logs <CONTAINER_ID> – czytanie logów z kontenera

Źródła

https://magnifier.pl/konteneryzacja-docker-kubernetes/
https://ncoder.pl/docker-podstawowa-idea-wirtualizacja-vs-konteneryzacja/
https://pl.wikipedia.org/wiki/Docker_(oprogramowanie)
https://github.com/xadhrit/xira
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.