Santhacklaus 2019 - Naughty Docker

Posted on mar. 31 décembre 2019 in CTF

It looks like a naughty developer has been deploying a Docker image on a Santa production server a few days before Christmas. He was in a rush and was not able to properly pass all security checks on the built Docker image. Would be a shame if this image could give you an SSH access to the production server... http://46.30.204.47"

Première étape alors, se rendre sur le site internet indiqué.

NaughtyDocker1

On a alors le nom de l'image Docker (santactf/app) ainsi que la commande pour le lancer. Ce que l'on fait de suite :).

$ docker run --rm -p 3000:3000 -d santactf/app
Unable to find image 'santactf/app:latest' locally
latest: Pulling from santactf/app
844c33c7e6ea: Pull complete 
ada5d61ae65d: Pull complete 
f8427fdf4292: Pull complete 
f025bafc4ab8: Pull complete 
7a9577c07934: Pull complete 
add4f74c413b: Pull complete 
1ee7a33fb93f: Pull complete 
08ab1881dcea: Pull complete 
96f3027f0dbd: Pull complete 
cb67eac57f41: Pull complete 
bf44330d5df8: Pull complete 
4932e843cace: Pull complete 
f0b9c596601c: Pull complete 
Digest: sha256:621c884f7ddd0351fbb114e0b9c1d4d3b0e309cb5c5efc9ce872fd201af79cad
Status: Downloaded newer image for santactf/app:latest
8c5aff2e1ca7ee420ed7599494d53a3d6fbdeab47f6a034c6c52ea2e6b3ba329
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
8c5aff2e1ca7        santactf/app        "docker-entrypoint.s…"   24 seconds ago      Up 17 seconds       0.0.0.0:3000->3000/tcp   festive_easley

Ok. On a notre docker de démarrer. Voyant ce que nous pouvons obtenir sur le port 3000 :

NaughtyDocker2

Rien de concluant en soit...

Essayons de voir le contenu du docker et le fonctionnement de l'application :

# On se connect directement en root via le -u 0
$ docker exec -u 0 -it 8c5aff2e1ca7 bash
root@8c5aff2e1ca7:/home/node# id
uid=0(root) gid=0(root) groups=0(root)
root@8c5aff2e1ca7:/home/node# ls
node_modules  package-lock.json  package.json  server.js
root@8c5aff2e1ca7:/home/node# ls -al
total 44
drwxr-xr-x  1 root root  4096 Dec 18 20:55 .
drwxr-xr-x  1 root root  4096 Dec 16 07:28 ..
-rw-r--r--  1 node node   220 May 15  2017 .bash_logout
-rw-r--r--  1 node node   675 May 15  2017 .profile
drwxr-xr-x 45 root root  4096 Dec 18 20:55 node_modules
-rw-r--r--  1 root root 12606 Dec 16 23:34 package-lock.json
-rw-r--r--  1 root root   241 Dec 16 23:34 package.json
-rw-r--r--  1 root root   458 Dec 18 20:53 server.js

C'est donc une application nodejs qui est lancé. Le fichier principal est donc server.js :

const fastify = require('fastify')({
    logger: true
});

fastify.get('/', function (request, reply) {
    reply.send('Some production Santa CTF app');
});

fastify.listen(3000, '0.0.0.0', function (err, address) {
    if (err) {
        fastify.log.error(err);
        process.exit(1);
    }
    fastify.log.info(`Server listening on ${address}`);
});

process.on('SIGTERM', function () {
    fastify.close(function(){
        process.exit(0);
    });
});

C'est juste un serveur web basique affichant la phrase vue plus haut. Rien côté fichier et dans le docker.

Regardons du coup comment ce docker est construit. On voit lors de sa récupération qu'il est composer de 13 couches en tout. N'ayant pas de dockerfile à disposition, la commande docker history peut nous aider à le reconstruire et comprendre l'enchaînement des commandes qui ont permis sa réalisation :

$  docker history --no-trunc santactf/app:latest
IMAGE                                                                     CREATED             CREATED BY                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    SIZE                COMMENT
sha256:ddde36e2209357c424cca26ac5a0b46c2f864be797c053bed700422177ba7261   11 days ago         /bin/sh -c #(nop)  CMD ["node" "server.js"]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   0B                  
<missing>                                                                 11 days ago         /bin/sh -c #(nop)  USER node                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  0B                  
<missing>                                                                 11 days ago         /bin/sh -c #(nop) COPY file:8b53431519dafa70baa13c0dd04861e8688090bfece040ae71244d2e14a66845 in /home/node/                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   458B                
<missing>                                                                 11 days ago         /bin/sh -c npm ci                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             5.59MB              
<missing>                                                                 11 days ago         /bin/sh -c #(nop) COPY multi:2f093554c78265fc6aeb1cb343015e8e8e7227fee6a0504f55721b9af13a16a6 in /home/node/                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  12.8kB              
<missing>                                                                 11 days ago         /bin/sh -c #(nop) WORKDIR /home/node                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          0B                  
<missing>                                                                 11 days ago         /bin/sh -c #(nop)  EXPOSE 3000                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                0B                  
<missing>                                                                 11 days ago         /bin/sh -c #(nop)  CMD ["node"]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               0B                  
<missing>                                                                 11 days ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entrypoint.sh"]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        0B                  
<missing>                                                                 11 days ago         /bin/sh -c #(nop) COPY file:6781e799bed1693e0357678a6692f346b66879c2248ff055a2ff51cc0a83288b in /usr/local/bin/                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               116B                
<missing>                                                                 11 days ago         /bin/sh -c ln -s /usr/local/bin/node /usr/local/bin/nodejs   && rm /home/node/.bashrc /home/node/.bash_history   && rm -rf /usr/share/prod-common                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             19B                 
<missing>                                                                 11 days ago         /bin/sh -c ARCH= && dpkgArch="$(dpkg --print-architecture)"   && case "${dpkgArch##*-}" in     amd64) ARCH='x64';;     ppc64el) ARCH='ppc64le';;     s390x) ARCH='s390x';;     arm64) ARCH='arm64';;     armhf) ARCH='armv7l';;     i386) ARCH='x86';;     *) echo "unsupported architecture"; exit 1 ;;   esac   && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz"   && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"   && tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner   && rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc                                                                                                                                                         67.2MB              
<missing>                                                                 11 days ago         /bin/sh -c #(nop) COPY dir:795933707ce316a3189ec6fd11f015b1acbc4eae6d5f01185625a86edaa2c5c4 in /                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              18.6kB              
<missing>                                                                 11 days ago         /bin/sh -c #(nop)  ENV NODE_VERSION=12.13.1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   0B                  
<missing>                                                                 11 days ago         /bin/sh -c groupadd --gid 1000 node   && useradd --uid 1000 --gid node --shell /bin/bash --create-home node                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   333kB               
<missing>                                                                 5 weeks ago         /bin/sh -c set -ex;  apt-get update;  apt-get install -y --no-install-recommends   autoconf   automake   bzip2   dpkg-dev   file   g++   gcc   imagemagick   libbz2-dev   libc6-dev   libcurl4-openssl-dev   libdb-dev   libevent-dev   libffi-dev   libgdbm-dev   libglib2.0-dev   libgmp-dev   libjpeg-dev   libkrb5-dev   liblzma-dev   libmagickcore-dev   libmagickwand-dev   libmaxminddb-dev   libncurses5-dev   libncursesw5-dev   libpng-dev   libpq-dev   libreadline-dev   libsqlite3-dev   libssl-dev   libtool   libwebp-dev   libxml2-dev   libxslt-dev   libyaml-dev   make   patch   unzip   xz-utils   zlib1g-dev     $(    if apt-cache show 'default-libmysqlclient-dev' 2>/dev/null | grep -q '^Version:'; then     echo 'default-libmysqlclient-dev';    else     echo 'libmysqlclient-dev';    fi   )  ;  rm -rf /var/lib/apt/lists/*   562MB               
<missing>                                                                 5 weeks ago         /bin/sh -c apt-get update && apt-get install -y --no-install-recommends   bzr   git   mercurial   openssh-client   subversion     procps  && rm -rf /var/lib/apt/lists/*                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      142MB               
<missing>                                                                 5 weeks ago         /bin/sh -c set -ex;  if ! command -v gpg > /dev/null; then   apt-get update;   apt-get install -y --no-install-recommends    gnupg    dirmngr   ;   rm -rf /var/lib/apt/lists/*;  fi                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          7.81MB              
<missing>                                                                 5 weeks ago         /bin/sh -c apt-get update && apt-get install -y --no-install-recommends   ca-certificates   curl   netbase   wget  && rm -rf /var/lib/apt/lists/*                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             23.2MB              
<missing>                                                                 5 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               0B                  
<missing>                                                                 5 weeks ago         /bin/sh -c #(nop) ADD file:152359c10cf61d80091bfd19e7e1968a538bebebfa048dca0386e35e1e999730 in /                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              101MB

Ah ! Enfin des choses intéressantes qui remontent. Il faut prendre du bas vers le haut. Les premières commandes mettent à jour le système et installent les dépendances. La suite est plus intéressantes :

/bin/sh -c ln -s /usr/local/bin/node /usr/local/bin/nodejs   && rm /home/node/.bashrc /home/node/.bash_history   && rm -rf /usr/share/prod-common

On remarque la suppression de deux fichiers et d'un dossier. Essayons donc de voir comment on peut récupérer le contenu :).

On cherchant de la documentation sur internet, on tombe sur cet article Medium : https://medium.com/@jessgreb01/digging-into-docker-layers-c22f948ed612. Il nous parle d'un dossier /var/lib/docker/aufs. Cependant, ce dossier n'existe pas sur mon serveur :

$ ls /var/lib/docker -l
total 48
drwx------  2 root root 4096 Sep 27 14:43 builder
drwx--x--x  4 root root 4096 Sep 27 14:43 buildkit
drwx------  4 root root 4096 Dec 30 17:21 containers
drwx------  3 root root 4096 Sep 27 14:43 image
drwxr-x---  3 root root 4096 Sep 27 14:43 network
drwx------ 35 root root 4096 Dec 30 17:21 overlay2
drwx------  4 root root 4096 Sep 27 14:43 plugins
drwx------  2 root root 4096 Dec 21 18:26 runtimes
drwx------  2 root root 4096 Sep 27 14:43 swarm
drwx------  2 root root 4096 Dec 30 17:21 tmp
drwx------  2 root root 4096 Sep 27 14:43 trust
drwx------  5 root root 4096 Sep 27 14:48 volumes

Cependant, un dossier overlay2 est présent et évoqué en fin d'article comme un possible successeur de AUFS. On vérfie lequel docker utilise :

$ docker info
Client:
 Debug Mode: false

Server:
 Containers: 2
  Running: 1
  Paused: 0
  Stopped: 1
 Images: 2
 Server Version: 19.03.5
 Storage Driver: overlay2

Ok, on sait donc qu'il faut non pas chercher sur AUFS mais sur overlay2. La page dans la documentation officielle docker nous aide sur son fonctionnement : https://docs.docker.com/storage/storagedriver/overlayfs-driver/#how-the-overlay2-driver-works.

Il nous faut donc retrouver le dossier prod-common ainsi que les deux fichiers .bashrc et .bash_history dans cette ensemble de dossier. Un find est c'est fait :) :

$ find . -name ".bash_history" | grep node
./beb357bfdfd498ff0fbb507996c034316381dc3a7c163890412f33fc3323c84b/diff/home/node/.bash_history
./71f44852a81b6d28dbf5c6d8d0d64857aa9d00ed5b647c4471c2e3df27cb5855/diff/home/node/.bash_history
$ wc -l ./71f44852a81b6d28dbf5c6d8d0d64857aa9d00ed5b647c4471c2e3df27cb5855/diff/home/node/.bash_history 
wc: ./71f44852a81b6d28dbf5c6d8d0d64857aa9d00ed5b647c4471c2e3df27cb5855/diff/home/node/.bash_history: No such device or address
$ wc -l ./beb357bfdfd498ff0fbb507996c034316381dc3a7c163890412f33fc3323c84b/diff/home/node/.bash_history
153 ./beb357bfdfd498ff0fbb507996c034316381dc3a7c163890412f33fc3323c84b/diff/home/node/.bash_history

BINGO ! On a retrouvé le dossier contenu les fichiers recherchés. Un tree sur le dossier nous le confirme :

$ tree -a
.
├── home
│   └── node
│       ├── .bash_history
│       └── .bashrc
└── usr
    └── share
        └── prod-common
            ├── dev_081219_backup.zip
            ├── dev_091219_backup.zip
            ├── dev_101219_backup.zip
            ├── dev_111219_backup.zip
            ├── dev_121219_backup.zip
            ├── dev_131219_backup.zip
            ├── dev_141219_backup.zip
            ├── dev_151219_backup.zip
            └── dev_161219_backup.zip

5 directories, 11 files

Le zip se trouvant dans le dossier prod-common peut indiqué la piste souhaité pour se connecter sur le serveur de production. La commande zipinfo va nous donner des informations sur leur contenu :

$ zipinfo usr/share/prod-common/dev_081219_backup.zip 
Archive:  usr/share/prod-common/dev_081219_backup.zip
Zip file size: 1290 bytes, number of entries: 2
-rw-------  3.0 unx      821 TX defN 19-Dec-18 21:31 id_santa_production
-rw-r--r--  3.0 unx      296 TX defN 19-Dec-18 21:31 id_santa_production.pub
2 files, 1117 bytes uncompressed, 872 bytes compressed:  21.9%

Parfait. Des clés SSH (publique et privée) pour y accéder. On extrait donc son contenu :

$ unzip dev_081219_backup.zip
Archive:  dev_081219_backup.zip
[dev_081219_backup.zip] id_santa_production password:

Cela ne pouvait pas être aussi facil :(. Regardons si nous pouvons avoir des indications ou même, le mot de passe dans un des deux autres fichiers que nous avons récupérer. Une recherche sur les deux fichiers avec pass peut nous mettre sur la voie :

$ grep -rHin 'pass' .
./.bash_history:49:vncpasswd
./.bash_history:50:vncpasswd -type
./.bash_history:54:vncpasswd -type Password
./.bash_history:55:vncpasswd -type "Password"
./.bash_history:115:zip --password "$ARCHIVE_PIN" "$PRODUCTION_BACKUP_FILE" id_santa_production*
$  grep -rHin 'ARCHIVE_PIN' .
./.bash_history:61:export ARCHIVE_PIN=25362
./.bash_history:115:zip --password "$ARCHIVE_PIN" "$PRODUCTION_BACKUP_FILE" id_santa_production*

Et c'est le cas. On a le code PIN pour l'archive mais on ne sait cependant pas quelle archive est rattachée à ce PIN. On peut donc essayer sur l'ensemble avec la commande find :

$ find usr/share/prod-common -exec unzip -P "25362" {} \;
unzip:  cannot find or open ., ..zip or ..ZIP.
Archive:  ./dev_081219_backup.zip
   skipping: id_santa_production     incorrect password
   skipping: id_santa_production.pub  incorrect password
Archive:  ./dev_131219_backup.zip
   skipping: id_santa_production     incorrect password
   skipping: id_santa_production.pub  incorrect password
Archive:  ./dev_151219_backup.zip
   skipping: id_santa_production     incorrect password
   skipping: id_santa_production.pub  incorrect password
Archive:  ./dev_161219_backup.zip
   skipping: id_santa_production     incorrect password
   skipping: id_santa_production.pub  incorrect password
Archive:  ./dev_141219_backup.zip
  inflating: id_santa_production     
  inflating: id_santa_production.pub  
Archive:  ./dev_121219_backup.zip
   skipping: id_santa_production     incorrect password
   skipping: id_santa_production.pub  incorrect password
Archive:  ./dev_091219_backup.zip
   skipping: id_santa_production     incorrect password
   skipping: id_santa_production.pub  incorrect password
Archive:  ./dev_101219_backup.zip
   skipping: id_santa_production     incorrect password
   skipping: id_santa_production.pub  incorrect password
Archive:  ./dev_111219_backup.zip
   skipping: id_santa_production     incorrect password
   skipping: id_santa_production.pub  incorrect password
$ ls -l
[...]
-rw------- 1 root root  821 Dec 18 21:31 id_santa_production
-rw-r--r-- 1 root root  296 Dec 18 21:31 id_santa_production.pub

Parfait. On a bien les clés. Mais on a toujours pas la commande SSH pour ce connecter. Comme pour le PIN, un grep suffit à retrouver l'information.

$ grep -rHin 'ssh' home/node/
.bash_history:56:sudo nano /etc/ssh/sshd_config
.bash_history:57:sudo service restart ssh
.bash_history:58:sudo service ssh restart
.bash_history:62:ls ~/.ssh
.bash_history:64:ssh-keygen -t rsa -C jmding0714@gmail.com
.bash_history:65:cd ~/.ssh/
.bash_history:129:nano ~/.ssh/authorized_keys
.bash_history:130:ssh -p 5700 rudolf-the-reindeer@46.30.204.47

On peut donc essayer de se connecter avec la clé et les informations trouvées :

$  ssh -p 5700 -i usr/share/prod-common/id_santa_production rudolf-the-reindeer@46.30.204.47
Enter passphrase for key 'usr/share/prod-common/id_santa_production': 

Bon. Nouveau mot de passe à trouvé. Cela ne pouvait pas être aussi simple :(. On reprend donc la recherche avec grep :

$ grep -rHin 'password' home/node/
home/node/.bash_history:54:vncpasswd -type Password
home/node/.bash_history:55:vncpasswd -type "Password"
home/node/.bash_history:115:zip --password "$ARCHIVE_PIN" "$PRODUCTION_BACKUP_FILE" id_santa_production*
$ grep -rHin 'pwd' home/node/
home/node/.bash_history:78:pwd
home/node/.bashrc:68:    export PRD_PWD='HoHoHo2020!NorthPole'

Et une fois de plus, c'est une victoire pour grep !

On relance notre commande ssh :

$  ssh -p 5700 -i usr/share/prod-common/id_santa_production rudolf-the-reindeer@46.30.204.47
Enter passphrase for key 'usr/share/prod-common/id_santa_production':


  ___   _   _  _ _____ _      ___ _____ ___
 / __| /_\ | \| |_   _/_\    / __|_   _| __|
 \__ \/ _ \| .` | | |/ _ \  | (__  | | | _| 
 |___/_/ \_\_|\_| |_/_/ \_\  \___| |_| |_|  


 Well done, the flag is SANTA{NeverTrustDockerImages7263}
 You may now log out of this server with "exit"


-bash-5.0$

Et c'est le FLAG !

https://i.giphy.com/media/VQ77RNKX0nyaA/giphy.webp

Très bon challenge pour découvrir et mieux comprendre le fonctionnement de Docker. Merci <3.