Find the RCE Sink: Spotting an eval() Injection in Python
Le défi
Cette API de 'calcul rapide' exécute tout ce que vous envoyez. Une fonction native de Python transforme ça en exécution de code à distance. Lisez les deux fichiers et tapez le nom de cette fonction.
Ce que tu vas apprendre
- Recognise eval()/exec() on untrusted input as a remote code execution sink
- Trace request data from its entry point to a dangerous function across files
- Tell apart code execution (eval) from safe data parsing (ast.literal_eval)
- Read a small codebase the way a reviewer does: follow the input, find the sink
Compétences testées
Prérequis
- Basic Python reading
- Familiarity with HTTP query parameters
Comment ça marche
Remote code execution (RCE) is the most severe class of web vulnerability: the attacker gets the application to run code of their choosing on the server. In Python, the classic sink is eval() (and its sibling exec()). eval() takes a string and evaluates it as a Python expression - so if any part of that string comes from the user, the user can run Python.
This app exposes a 'quick math' endpoint. It reads expr from the query string and hands it directly to eval(). A normal request like ?expr=2*21 returns 42, which is exactly why this kind of bug ships - it looks like a harmless calculator. But an attacker sends ?expr=__import__('os').system('id') and the server runs a shell command. From there it is a short step to reading files, exfiltrating data, or planting a shell.
The fix is in the second file. ast.literal_eval() parses only Python literals - numbers, strings, lists, dicts - and refuses anything that would execute. That is the right tool when you need to turn text into data. The job in a review like this is to follow the untrusted value (expr) from where it enters to what consumes it, and recognise that the consumer is a code-execution primitive.
Erreurs fréquentes
- Assuming a 'calculator' is harmless. The friendly feature name hides that eval runs arbitrary code, not just arithmetic.
- Pointing at request.args instead of the sink. Reading input is fine; the bug is what you do with it. Name the function that executes it.
- Confusing eval with literal_eval. They look similar but only one executes code - that difference is the whole vulnerability.
Comment s'en protéger
Never pass untrusted input to eval() or exec(). If you need arithmetic, parse it with a real expression parser or a safe library. If you need to turn text into data, use ast.literal_eval() or json.loads().
- Ban
eval/execon request data in code review and with a linter rule. - Treat every query parameter, header, and body field as attacker-controlled.
- Prefer allow-lists and typed parsing over evaluating strings.