Voltar as noticias
sympy.parse_expr pode executar comandos do sistema se não for controlado
MCP ProtocolMediaEN

sympy.parse_expr pode executar comandos do sistema se não for controlado

Dev.to - MCP·18 de maio de 2026

sympy.parse_expr irá executar os.system se você permitir. Aqui está o portão AST que me impediu de enviar o RCE.

Eu estava construindo um servidor MCP que aceita uma fórmula de medição como uma string de um LLM, a analisa com sympy e a avalia via Monte Carlo. Cinco minutos de integração. Treze testes. Doze passaram.

O décimo terceiro foi um teste de segurança. A fórmula que passou foi:

"__import__('os').system('echo PWNED')"

O teste esperava um ValueError. Em vez disso, recebi uma mensagem de falha no teste e a string PWNED impressa no meu terminal.

Deixe-me deixar isso claro. sympy.parse_expr, com os argumentos padrão que eu estava usando, realmente invocou os.system na minha máquina. Meu próprio parser, rodando no meu próprio processo de teste, executou um shell e ecoou texto no meu terminal. Se eu tivesse enviado isso para produção, qualquer usuário de LLM com um prompt suficientemente criativo poderia ter feito esse mesmo shell-out acontecer em uma instância do Cloud Run sob minha conta de cobrança.

Por que isso acontece

sympy.parse_expr é implementado em cima do eval() do Python. Quando você não bloqueia explicitamente o dicionário global em que ele avalia, eval herda os __builtins__ do módulo atual, que inclui __import__, open, compile, exec, getattr, e amigos. Uma string que parece uma expressão matemática, mas que faz referência a __import__, é resolvida como uma expressão Python — e é executada.

Isso é um comportamento documentado do sympy com avisos na documentação. Avisos não salvam você quando você está se movendo rápido no dia do lançamento.

A superfície de vulnerabilidade é mais ampla do que __import__. Qualquer um desses pode dar a um atacante execução de código dependendo do que está em escopo:

  • __import__('os').system(...) — shell direto
  • __builtins__.__dict__['eval'](...) — reentrar no eval
  • (0).__class__.__bases__[0].__subclasses__() — escapar para instâncias de classe arbitrárias
  • getattr(__builtins__, 'open')('/etc/passwd').read() — leitura de arquivo
  • ''.join.__globals__['__builtins__']['exec']('...') — exec via método de string walk

A correção que realmente funciona: valide no nível AST antes que o sympy veja a string

import ast

_DANGEROUS_NAMES = frozenset({
    "__import__", "__builtins__", "__class__", "__subclasses__",
    "eval", "exec", "compile", "open", "globals", "locals",
    "getattr", "setattr", "delattr", "exit", "quit",
})

_FORBIDDEN_AST_NODES = (
    ast.Attribute,    # bloqueia "os.system" e "obj.__class__"
    ast.Subscript,    # bloqueia "x[0]" indexando em dunders
    ast.Lambda,
    ast.GeneratorExp,
    ast.ListComp,
    ast.DictComp,
    ast.Starred,
    ast.JoinedStr,
    ast.FormattedValue,
    ast.NamedExpr,
)

def _validate_formula_ast(formula: str) -> None:
    tree = ast.parse(formula, mode="eval")
    for node in ast.walk(tree):
        if isinstance(node, _FORBIDDEN_AST_NODES):
            raise ValueError(
                f"Construto de fórmula não permitido: {type(node).__name__}"
            )
        if isinstance(node, ast.Name):
            if node.
Contexto Triplo Up

Empresas brasileiras que utilizam LLMs para processamento de fórmulas devem estar cientes das vulnerabilidades associadas ao uso de bibliotecas como sympy. A implementação de validações adequadas é crucial para evitar a execução de código malicioso em ambientes de produção.

Noticias relacionadas

Gostou do conteudo?

Receba toda semana as principais novidades sobre WebMCP.