HTB - pourquoi ajouter un domaine dans /etc/hosts en CTF ?
Résumé
Ce premier post sur mon blog explique la raison pour laquelle, dans certaines situations sur Hack the box (ou autre CTF), il faut ajouter au fichier /etc/hosts un domaine associé à la machine attaquée.
J’apporte dans ce post deux réponses. D’abord une réponse courte et simple et ensuite une réponse détaillée qui explique exactement ce qu’il se passe sous le capot.
Contexte
Le scénario classique quand on fait un test de pénétration sur Hack the box c’est une machine avec deux ports ouverts :
- 22 - ssh
- 80 - serveur web
En fonction de la box, plusieurs sites peuvent se cacher derrière la même IP et le même port. C’est dans ce cas que l’utilisation du fichier /etc/hosts est indispensable. Pourquoi ?
Réponse courte
La raison se trouve dans la configuration des serveurs web.
Comment héberger plusieurs sites web derrière une seule adresse IP et un seul port ? Dans le contexte des CTF, la réponse est généralement celle des virtual hosts.
Les serveurs web (Apache, Nginx, etc.) utilisent le contenu de la requête HTTP (plus précisément l’en-tête ‘Host’) pour déterminer quel site doit être renvoyé au client. En fonction de cette valeur, le serveur peut servir un site totalement différent, même si l’IP et le port sont identiques.
Lorsqu’on accède directement à une machine via son IP, l’en-tête ‘Host’ contient cette IP. Mais dans de nombreux CTF, le serveur web attend un nom de domaine précis (par exemple box.htb) pour servir le bon site.
Ajouter ce domaine dans le fichier /etc/hosts permet :
- de forcer la résolution DNS vers l’IP de la machine,
- d’envoyer automatiquement la bonne valeur dans l’en-tête ‘Host’,
- et donc d’accéder au site web attendu par le serveur.
Réponse plus détaillée
Pour être le plus clair possible, je prends l’exemple de la box easy sur Hack the box Soulmate.
Mise en situation
Sur Soulmate, le scan du serveur nous montre 2 ports ouverts :
1
2
3
4
5
6
7
8
9
10
11
12
Nmap scan report for 10.10.11.86
Host is up (0.029s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_ 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://soulmate.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap informe qu’il n’a pas pu résoudre le champ ‘http-title’ pour cette raison : Did not follow redirect to http://soulmate.htb/. On peut utiliser curl pour interroger à notre tour le serveur web :
1
2
┌──(root㉿kali)-[~]
└─# curl -v http://10.10.11.86
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
* Trying 10.10.11.86:80...
* Established connection to 10.10.11.86 (10.10.11.86 port 80) from 10.10.14.214 port 52434
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 10.10.11.86
> User-Agent: curl/8.17.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 302 Moved Temporarily
< Server: nginx/1.18.0 (Ubuntu)
< Date: Sun, 28 Dec 2025 14:39:19 GMT
< Content-Type: text/html
< Content-Length: 154
< Connection: keep-alive
< Location: http://soulmate.htb/
<
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
* Connection #0 to host 10.10.11.86:80 left intact
La connexion au serveur web fonctionne mais nous renvoie une 302 : c’est une redirection vers le champ “Location: “, soit vers http://soulmate.htb. Le serveur web redirige volontairement vers le nom de domaine pour forcer l’utilisation du virtual host attendu.
Qu’est-ce que les virtual hosts ?
Pourquoi diable le serveur nous redirige vers http://soulmate.htb et ne nous affiche pas le site directement ?
Les hébergeurs de sites web ont, à travers le temps, rapidement eu le besoin d’avoir plusieurs sites derrière la même adresse IP et le même port. Si je prends mon propre exemple, je n’ai qu’un seul serveur et par la même occasion qu’une seule adresse IP publique. Si je veux proposer derrière mon domaine paupaul.fr plusieurs services (ce blog, un guacamole, un forejo etc.), mon serveur doit être capable de différencier quel service renvoyer en fonction de la requête reçue. J’utilise pour cela un reverse proxy qui détecte quel domaine souhaite l’utilisateur (ex : guacamole.paupaul.fr) et qui envoie la requête à ma machine qui héberge guacamole.
Dans le contexte de Hack the box, c’est très similaire. Les serveurs webs comme Apache et Nginx permettent de faire du virtual hosting. On configure dans un fichier que le serveur web doit renvoyer ‘indexService1.html’ quand il reçoit service1.example.com comme requête.
Exemple de fichier de configuration :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# BLOC 1 : Catch-All (répond quand on accède au serveur via l'IP ou n'importe quel autre sous-domaine différent de 'box.htb')
server {
listen 80 default_server;
server_name _;
root /var/www/html;
index index.html;
}
# BLOC 2 : Virtual Host du CTF (répond quand le champ Host est précisément box.htb)
server {
listen 80;
server_name box.htb;
root /var/www/box;
index index.html;
}
Dans cet exemple, le catch-all (virtual host par défaut) attrape tout ce qui est différent de box.htb et renvoie /var/www/html/index.html. Si la requête demande box.htb, Nginx renvoie le bon site web : /var/www/box/index.html.
Donc : le virtual hosting permet à un seul serveur physique d’héberger plusieurs sites web distincts en utilisant une seule adresse IP.
Comment ça fonctionne
Le protocole HTTP, comme tous les autres protocoles, est créé sous plusieurs règles strictes définies dans les RFC. Les RFC définissent notamment que chaque requête doit s’accompagner de header. Les serveurs web peuvent utiliser notamment l’en-tête obligatoire '’Host’‘ pour diriger chaque requête vers le bon site ou la bonne application. Pour info : en HTTPS, le même principe existe au niveau TLS avec le SNI (Server Name Indication), utilisé avant même l’échange HTTP.
Dans le contexte de Hack the box
On a vu sur le Nmap et avec curl qu’on peut joindre le site http://10.10.11.86 mais que nous sommes redirigés. C’est la règle catch-all (la règle par défaut) de la configuration du serveur web qu’on attaque.
Si on veut accéder au site web, il faut qu’on change la valeur de l’en-tête ‘Host’ dans notre requête HTTP pour correspondre à ce que le serveur web attend. Avec l’exemple de Soulmate, puisque nous sommes redirigés vers http://soulmate.htb, on sait qu’il nous faut ce domaine dans la valeur de l’en-tête ‘Host’: pour que le serveur web nous renvoie le site web.
Lors de notre premier test avec curl, la requête HTTP ressemblait à :
1
2
3
4
5
6
7
8
9
┌──(root㉿kali)-[~]
└─# curl -v http://10.10.11.86
* Trying 10.10.11.86:80...
* Established connection to 10.10.11.86 (10.10.11.86 port 80) from 10.10.14.214 port 52434
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 10.10.11.86
> User-Agent: curl/8.17.0
> Accept: */*
La valeur de l’en-tête ‘Host’ n’est pas bonne. Donc, pour que le serveur renvoie le site, on modifie l’en-tête ‘Host’ dans la commande curl :
1
2
┌──(root㉿kali)-[~]
└─# curl -v -H "Host: soulmate.htb" http://10.10.11.86
Requête engendrée :
1
2
3
4
5
6
7
* Trying 10.10.11.86:80...
* Established connection to 10.10.11.86 (10.10.11.86 port 80) from 10.10.14.214 port 39210
* using HTTP/1.x
> GET / HTTP/1.1
> Host: soulmate.htb
> User-Agent: curl/8.17.0
> Accept: */*
La valeur de ‘Host’ est bien modifiée. Réponse du serveur web :
1
2
3
4
5
6
7
8
9
10
11
12
13
* Request completely sent off
< HTTP/1.1 200 OK
< Server: nginx/1.18.0 (Ubuntu)
< Date: Sun, 28 Dec 2025 15:10:53 GMT
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Set-Cookie: PHPSESSID=bfj5o5bus6o9315j1hrc2jjp0a; path=/
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate
< Pragma: no-cache
<
<!DOCTYPE html>
On reçoit une HTTP 200 : la requête a réussi et le site est envoyé (dans le DocType).
Pourquoi le /etc/hosts ?
On vient de voir avec curl qu’on peut accéder au site directement avec son IP, sans passer par le domaine. Pourquoi on ajoute alors le domaine dans le fichier /etc/hosts ?
Dans notre navigateur, quand on va sur un site web, on entre son URL. Sur internet, c’est le DNS qui résout le nom de domaine et qui nous renvoie l’adresse IP du serveur qu’on veut joindre. C’est essentiel car notre système doit encapsuler la requête dans un paquet IP avec l’adresse IP du serveur web qu’on veut joindre.
Le problème c’est que quand on fait un CTF, les serveurs DNS ne connaissent pas les domaines des CTF et ne peuvent pas les résoudre. Quand on utilise curl pour interroger le serveur web avec son IP, Linux sait déjà quel IP utiliser pour créer le paquet. Si on utilise le navigateur et qu’on se rend sur http://soulmate.htb, la valeur de l’en-tête ‘Host’ sera bien soulmate.htb mais Linux ne saura pas comment créer le paquet IP. Il interrogera un serveur DNS sur internet qui n’aura aucune connaissance de soulmate.htb.
On ajoute donc dans le fichier /etc/hosts le domaine, ce qui permet au navigateur de créer une requête avec la bonne valeur de l’en-tête ‘Host’ et au système de créer un paquet avec une IP qu’il connaît.
Conclusion
Avoir ces informations en tête permet de gagner beaucoup de temps. Dès le scan avec Nmap, s’il informe qu’une redirection est faite par le serveur web, on peut raisonnablement penser qu’il est configuré avec des virtual hosts. On déduit alors que plusieurs sites / services sont disponibles derrière la même IP et le même port.
En parallèle de la reconnaissance du site, faire un brute force de ces virtual hosts est donc pertinent. Pour cela, on peut utiliser l’outil ffuf, capable de générer des requêtes avec des en-têtes modifiées :
1
2
┌──(root㉿kali)-[~]
└─# ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -H "Host: FUZZ.soulmate.htb" -u http://10.10.11.86
Résultat :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.10.11.86
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt
:: Header : Host: FUZZ.soulmate.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
store [Status: 302, Size: 154, Words: 4, Lines: 8, Duration: 33ms]
gw [Status: 302, Size: 154, Words: 4, Lines: 8, Duration: 31ms]
www2 [Status: 302, Size: 154, Words: 4, Lines: 8, Duration: 31ms]
admin [Status: 302, Size: 154, Words: 4, Lines: 8, Duration: 31ms]
www [Status: 302, Size: 154, Words: 4, Lines: 8, Duration: 31ms]
mail [Status: 302, Size: 154, Words: 4, Lines: 8, Duration: 31ms]
...
...
On filtre ensuite, par exemple, sur toutes les requêtes de mots ‘4’s :
1
ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -H "Host: FUZZ.soulmate.htb" -u http://10.10.11.86 -fw 4
1
2
...
ftp [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 34ms]
Un nouveau domaine est découvert et le site derrière peut être attaqué.
Donc, dès qu’un site se comporte différemment selon l’en-tête Host, il faut penser à :
- ajouter le domaine dans
/etc/hosts, - tester des en-têtes
Hostpersonnalisés, - et envisager un brute force de virtual hosts.
Ce sont des réflexes de base en CTF. Dans de nombreux cas, une surface d’attaque entière reste invisible tant que le bon nom de domaine n’est pas utilisé.