Dojo #46

Challenge "Ghost Wisper" du dojo de YesWeHack (novembre 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

A critical Remote Code Execution (RCE) vulnerability was identified in the application’s command-execution workflow. The issue arises from a flawed Unicode-normalization-based sanitization process combined with unescaped interpolation into a system command executed via ``os.popen()`.

The application attempts to block dangerous characters (notably single quotes ') by replacing them and applying Unicode normalization (unicodedata.normalize("NFKC")). However, Unicode full-width characters, such as Fullwidth Apostrophe (U+FF07), are not filtered before normalization, and normalization maps them back to dangerous ASCII characters. This allows an attacker to inject arbitrary shell command separators such as '; and achieve full command execution.

The injected payload is then propagated into both the command and the Jinja2 template, leading to command execution on the host system.

This report describes the vulnerability, provides technical details and exploit flow, root cause analysis, impact assessment, remediation guidelines, and a Proof-of-Concept (PoC) demonstrating the RCE.


✍ Code

import os, unicodedata
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')

def main():
    whisperMsg = unquote("")

    # Normalize dangerous characters
    whisperMsg = unicodedata.normalize("NFKC", whisperMsg.replace("'", "_"))

    # Run a command and capture its output
    with os.popen(f"echo -n '{whisperMsg}' | hexdump") as stream:
        hextext = f"{stream.read()} | {whisperMsg}"
        print( template.render(msg=whisperMsg, hextext=hextext) )

main()

🔍 Technical Details

VID-01 — Unicode Normalization Bypass Leading to Command Injection

Vulnerability type: Command Injection Affected components: unicodedata.normalize(), shell invocation (os.popen) Severity: Critical

User-controlled input (whisperMsg) is passed through:

whisperMsg = unicodedata.normalize("NFKC", whisperMsg.replace("'", "_"))

The intended mitigation is to remove ASCII ' characters. However, the payload uses Fullwidth Apostrophe U+FF07: ', which:

  1. Bypasses .replace("'", "_"), because ' ≠ '
  2. Is normalized by NFKC into ', reintroducing the dangerous character
  3. Gets inserted unescaped into:
os.popen(f"echo -n '{whisperMsg}' | hexdump")
  1. Breaks out of the quoted shell string and injects arbitrary OS commands.
whisperMsg = unquote("<input>")
whisperMsg = unicodedata.normalize("NFKC", whisperMsg.replace("'", "_"))

with os.popen(f"echo -n '{whisperMsg}' | hexdump") as stream:
    hextext = f"{stream.read()} | {whisperMsg}"
    print(template.render(msg=whisperMsg, hextext=hextext))

🧠 Root Cause

1. Misunderstanding Unicode normalization

The application incorrectly assumes that replacing ' before NFKC normalization eliminates dangerous metacharacters. NFKC canonicalizes several Unicode punctuation marks into ASCII equivalents, including:

Unicode Description Normalized to
U+FF07 Fullwidth Apostrophe ' '
U+FE64 Small Less-Than Sign <
U+FE65 Small Greater-Than Sign >

The following python script was used in order to get the unicode caracters that are equal to quote after normalization.

import unicodedata

equivalents_quote = []

for codepoint in range(0x110000):
    char = chr(codepoint)
    try:
        # NFKC Normalization
        normalized = unicodedata.normalize("NFKC", char)
        if normalized == "'":
            equivalents_quote.append((char, f"U+{codepoint:04X}", unicodedata.name(char, "UNKNOWN")))
    except Exception:
        continue

for char, code, name in equivalents_quote:
    print(f"{char} ({code}) -> {name}")

print(f"\nTotal equivalent found : {len(equivalents_quote)}")

Output: Terminal : le script Python liste les deux caractères Unicode normalisés en apostrophe

2. Unescaped embedding into shell commands

User input is placed directly into:

f"echo -n '{whisperMsg}' | hexdump"

which is unsafe unless properly escaped or avoided entirely.

3. Command execution through os.popen()

os.popen() spawns a shell (/bin/sh -c "..."), allowing shell metacharacters to be interpreted.

Combined, these flaws produce a reliable RCE primitive.

💥 Impact

A remote attacker can obtain arbitrary code execution inside the server environment. Potential consequences include:

  • Full server compromise
  • Reading environment variables (API keys, DB credentials, tokens)
  • Stealing application secrets
  • Lateral movement inside the internal network
  • Deploying reverse shells or persistence implants
  • Privilege escalation if the runtime user has elevated permissions

Because the attack occurs before template rendering, WAF or template-sandbox protections do not mitigate the vulnerability.

🛡️ Recommendations

✔ Do not embed user input in shell commands Replace os.popen with safe APIs:

subprocess.run([...], shell=False)

Or avoid shell execution entirely if not required

✔ Escape or remove dangerous characters

Apply strict allowlisting (e.g., alphanumeric + safe punctuation only). Do not rely on Unicode normalization for security filtering.

✔ Sanitize before normalization

If normalization is required for functional reasons, sanitize both: - Before normalization - After normalization

✔ Enforce strict Content Security and Template boundary controls

Avoid injecting attacker data directly into templates unless properly escaped.

✔ Add unit tests for Unicode security regressions

Specifically test NFKC/NFD/NFKD mappings of problematic characters.

✅ Proof of Concept (PoC)

  • Original payload : '; env ;# '
  • After flawed sanitization: '; env ;# '

Dojo YesWeHack : la payload injectée affiche les variables d'environnement dont le flag

How the injection works

  1. The fullwidth apostrophe ' bypasses filtering.
  2. Normalization converts it into '.
  3. The final shell command becomes:
echo -n ''; env ;# ' | hexdump

This executes:

env

and leaks full environment variables, potentially including sensitive credentials.

FLAG

FLAG{Gh0s7_S4y_BOOO000oooo}

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