Encontre o Sink de RCE: Identificando uma Injeção eval() em 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
- Reconhecer eval()/exec() com entrada não confiável como um sink de execução remota de código
- Rastrear dados da requisição desde o ponto de entrada até uma função perigosa, entre arquivos
- Diferenciar execução de código (eval) de parsing seguro de dados (ast.literal_eval)
- Ler uma base de código pequena como um revisor faria: seguir a entrada, encontrar o sink
Habilidades testadas
Pré-requisitos
- Leitura básica de Python
- Familiaridade com parâmetros de query HTTP
Como funciona
Execução remota de código (RCE) é a classe mais grave de vulnerabilidade web: o atacante faz a aplicação executar código de sua escolha no servidor. Em Python, o sink clássico é eval() (e sua irmã exec()). eval() recebe uma string e a avalia como uma expressão Python - então, se qualquer parte dessa string vier do usuário, o usuário pode executar Python.
Esta aplicação expõe um endpoint de 'cálculo rápido'. Ele lê expr da query string e passa diretamente para eval(). Uma requisição normal como ?expr=2*21 retorna 42, que é exatamente por que esse tipo de bug passa despercebido - parece uma calculadora inofensiva. Mas um atacante envia ?expr=__import__('os').system('id') e o servidor executa um comando shell. Daí é um pequeno passo até ler arquivos, exfiltrar dados ou plantar um shell.
A correção está no segundo arquivo. ast.literal_eval() analisa apenas literais Python - números, strings, listas, dicts - e recusa qualquer coisa que executaria código. Essa é a ferramenta certa quando você precisa transformar texto em dados. A tarefa em uma revisão como essa é seguir o valor não confiável (expr) desde onde entra até o que o consome, e reconhecer que o consumidor é um primitivo de execução de código.
Erros comuns
- Assumir que uma 'calculadora' é inofensiva. O nome amigável da funcionalidade esconde que eval executa código arbitrário, não apenas aritmética.
- Apontar para request.args em vez do sink. Ler a entrada não é o problema; o bug está no que você faz com ela. Nomeie a função que a executa.
- Confundir eval com literal_eval. Parecem similares, mas só uma delas executa código - essa diferença é toda a vulnerabilidade.
Como se proteger
Nunca passe entrada não confiável para eval() ou exec(). Se precisar de aritmética, faça o parsing com um parser de expressões de verdade ou uma biblioteca segura. Se precisar transformar texto em dados, use ast.literal_eval() ou json.loads().
- Proíba
eval/execem dados de requisição na revisão de código e com uma regra de linter. - Trate todo parâmetro de query, header e campo do body como controlado pelo atacante.
- Prefira allow-lists e parsing tipado em vez de avaliar strings.