Como se proteger de um ataque em método rename() do PHP?

O último post nós mostramos como você pode explorar um ataque no médoto PHP rename().

Você pode ver mais detalhes sobre rename() na documentação do PHP.

E pode ver sobre o último post ( Hacking usando método “rename()” do PHP ) para entender melhor esse post.

Em resumo o método rename aceita dois parâmetros, o primeiro com nome antigo e o segundo com o novo nove e com os dois é efetuado a troca do nome do arquivo.

Depois de aprender como fazemos o ataque, nós vamos fazer um código defensivo.

Em primeiro lugar, você sempre vai ler o tempo todo em meus posts que não existe bala de prata, uma única solução.

Existe muitos caminhos para salvar seu code quando trabalhar com manipulação de arquivos. Nesse post eu vou mostrar como you pode proteger usando listas.

Em teoria você vai criar lista com extensões para serem aceitas ou não.

Exemplo do código exposto:

Obs: Eu estou usando método GET por ser mais simples de ensinar, mas pode ser por qualquer método http.

http://www.target.com/?oldImage=imageX.jpg

$_GET['oldImage'] = 'imageX.jpg';
<?php 
$newName = 'imageNewName.jpg';
rename( $_GET['oldImage'], $newName );
?>

Exemplo de proteção usando white list( listas de extensões de arquivos que são autorizados, nesse caso renomeados );

$oldImage = basename( $_GET['oldImage'] );
$newName = 'imageNewName.jpg';
$pathFolder = '/uploads/';

$validExtensions = [
'jpg',
'jpeg',
'png'
];
$ext = pathinfo($oldImage, PATHINFO_EXTENSION);
if(in_array($ext,$validExtensions)){
rename( $pathFolder.$oldImage, $pathFolder.$newName );
}

Primeiro digo para pegar apenas nome do arquivo usando basename, eliminando a navegação entre pastas. Digo para ele para trabalhar apenas um local específico, nesse caso a pasta uploads.

Nesse exemplo eu insiro possíveis extensões para renomear. E o código diz, você só pode renomear arquivos com essas extensões listadas.

Exemplo de proteção usando black list ( lista de extensões que não são autorizadas, no caso renomear );

$oldImage = basename( $_GET['oldImage'] );
$newName = 'imageNewName.jpg';
$pathFolder = '/uploads/';

$validExtensions = [
    'php',
    'js',
    'env'
];

$ext = pathinfo($oldImage, PATHINFO_EXTENSION);
if( ! in_array($ext,$validExtensions) ){
    rename( $pathFolder.$oldImage, $pathFolder.$newName );
}

Nesse exemplo ele também utiliza a mesma estratégia de renomear apenas arquivos de uma pasta específica.

Nesse exemplo eu insiro extensões that não podem ser renomeadas e mudo a validação do if com !. O código diz “se não existe essa extensão você pode renomear”.

Qual opção é mais segura?

A white list, porque a white list força você renomear somente extensões específicas. Com black list, você pode ter muitas extensões e acabar esquecendo de algumas que podem ser importantes para seu sistema.

Dicas:

Use métodos que resolvam seu problema por você ( não é necessário inventar a roda ), como pathinfo com parâmetro PATHINFO_EXTENSION. Ele retorna a extensão do nome. Ou métodos filters, como filter_var, filter_input.

Eu sei que você pode criar seu método para fazer isso, mas existe formas de efetuar bypass em defesas erradas ( assunto para próximo post ) e métodos do core que geralmente pensam na maioria das possibilidades para você.

Obs: Muitas pessoas falaram sobre o filter_var and filter_input ajudaria a inibir a navegação entre pastas. Em primeiro momento eu disse que só isso não resolvia, mas eu estava errado =). Sim você consegue inibir essa navegação usando filter_var com parâmetro FILTER_SANITIZE_ENCODED, ele muda as barras “/” para “%2F”, impedindo a navegação.

Exemplo com filter:

$oldImage = filter_var( $_GET['oldImage'] , FILTER_SANITIZE_ENCODED));

Espere pelas formas erradas de se proteger e os bypass no próximo post.

E aí, o que achou? Tem algum bypass para nos mostrar? Ou uma outra forma de se defender? Fala aí…

Quer receber os avisos de novos posts? Cadastre em nossa Newsletter.

Vejo vocês no próximo post pessoal.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *