Find the RCE Sink: Spotting an eval() Injection in Python
O desafio
Esta API de 'cálculo rápido' executa tudo que você envia. Uma função nativa do Python transforma isso em execução remota de código. Leia os dois arquivos e digite o nome dessa função.
O que você vai aprender
- 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
Habilidades testadas
Pré-requisitos
- Basic Python reading
- Familiarity with HTTP query parameters
Como funciona
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.
Erros comuns
- 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.
Como se proteger
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.